Dealing with HTTP (Url) Query Strings in PHP
There is a new package in town called query-string. It allows to create, access and manipulate query strings for HTTP requests in a very convenient way. Here's a quick overview of what you can do with it and also how it can be used via the url package.
The last months I started thinking about improving how you can change a URL's query string. It all started with this tweet as an answer to @heychazza's tweet about a nice way to build URLs in javascript.
Then last week someone added this github issue for the url package, and it got me thinking more about this. I liked the suggested API to get and set query params, but I found that it's not enough for more complex query strings. As query strings are also used in POST requests and sent in the request body, I now finally added a separate query-string package and also implemented it in the url package.
Implementation in the Url Package
First off: I set the required PHP version for the new package to 8.0 as the last 7.x version (7.4) is already in the final "security fixes only" phase. The url package currently still requires only 7.2. As I probably plan another BC break for v2 of the url package, for now I just added the query-string package as suggestion to the composer.json. You can manually install it, when you're already on PHP 8.x and want to use the advanced query string functionality.
When you've installed it via
composer require crwlr/query-string
the new queryString()
method of the Url
class returns an instance of the Query
class shipped with the new package. Here's a quick usage example:
$url = Url::parse('https://www.example.com/listing?page[number]=3&page[size]=25');
$url->queryString()
->get('page')
->set('number', '4');
var_dump($url->__toString());
// string(68) "https://www.example.com/listing?page%5Bnumber%5D=4&page%5Bsize%5D=25"
Standalone Usage
If you want to parse query strings standalone, not in the URL context, you can create an instance of the Query
class from string or from array:
$query = Query::fromString('foo=bar&baz=quz');
$query = Query::fromArray(['foo' => 'bar', 'baz' => 'quz']);
Access
Here a quick example of different ways how to access query string params:
$fooValue = Query::fromString('foo=bar&baz=quz')->get('foo'); // string(3) "bar"
When the requested key is an array, the get()
method returns another (child) Query
instance that you can query further:
$fooBazValue = Query::fromString('foo[bar]=1&foo[baz]=2&foo[quz]=3')
->get('foo')
->get('baz'); // string(1) "2"
You can check if a certain key exists in the query:
$query = Query::fromString('foo=1&bar=2');
$query->has('bar'); // bool(true)
$query->has('baz'); // bool(false)
You can get the first or last element of an indexed array:
$query = Query::fromString('foo[]=1&foo[]=2&foo[]=3');
$query->first('foo'); // string(1) "1"
$query->last('foo'); // string(1) "3"
You can check if the value for a certain key is an array of a scalar value:
$query = Query::fromString('foo[]=1&foo[]=2&bar=3');
$query->isArray('foo'); // bool(true)
$query->isScalar('foo'); // bool(false)
$query->isArray('bar'); // bool(false)
$query->isScalar('bar'); // bool(true)
And of course you can then convert the query to a string or to an array again:
$query = Query::fromString('foo=bar&baz=quz');
$queryArray = $query->toArray();
// array(2) {
// ["foo"]=>
// string(3) "bar"
// ["baz"]=>
// string(3) "quz"
// }
$query = Query::fromArray(['foo' => 'bar', 'baz' => 'quz']);
$queryString = $query->toString(); // string(15) "foo=bar&baz=quz"
Manipulation
You can set a certain key:
$query = Query::fromString('foo=bar')->set('baz', 'quz');
// string(15) "foo=bar&baz=quz"
Also to an array:
$query = Query::fromString('foo=1&bar=2')
->set('baz', ['3', '4']);
// string(29) "foo=1&bar=2&baz[0]=3&baz[1]=4"
You can also append values to an existing array:
$query = Query::fromString('foo[]=1&foo[]=2')
->appendTo('foo', '3');
// string(26) "foo[0]=1&foo[1]=2&foo[2]=3"
$query = Query::fromString('foo[bar]=1&foo[baz]=2')
->appendTo('foo', ['quz' => '3']);
// string(32) "foo[bar]=1&foo[baz]=2&foo[quz]=3"
Remove keys or values from keys:
$query = Query::fromString('foo[]=1&foo[]=2&bar=3&baz=4')
->remove('foo');
// string(11) "bar=3&baz=4"
$query = Query::fromString('foo[]=1&foo[]=2&foo[]=3&foo[]=2')
->removeValueFrom('foo', '2');
// string(17) "foo[0]=1&foo[1]=3"
And you can filter or map queries with callback functions:
$query = Query::fromString('no1=12&no2=7&no3=23&no4=9&no5=10')
->filter(function ($value, $key) {
return (int) $value >= 10;
});
// string(20) "no1=12&no3=23&no5=10"
$query = Query::fromString('foo=1&bar=2&baz=3&quz=4')
->map(function ($value) {
return (int) $value + 3;
});
// string(23) "foo=4&bar=5&baz=6&quz=7"
For more details have a look at the documentation. If you're having any question or issues, don't be shy and reach out on twitter or github.