The Query

The library provides a League\Uri\Components\Query class to ease query string creation and manipulation. This URI component object exposes the package common API, but also provide specific methods to work with the URI query component.

If the modifications do not change the current object, it is returned as is, otherwise, a new modified object is returned.

If the submitted value is not valid a League\Uri\Exceptions\SyntaxError exception is thrown.

Standard instantiationΒΆ

Using an RFC compliant algorithm

<?php
public static Query::new(Stringable|string|null $value = null): self
public static Query::fromUri(): self
public static Query::fromRFC3986(Stringable|string $value, string $separator = '&'): self
public static Query::fromRFC1738(Stringable|string $value, string $separator = '&'): self
public static Query::fromFormData(Stringable|string $value, string $separator = '&'): self
copy πŸ“‹
  • new and fromRFC3986 instantiates a query string encoded using RFC3986 query component rules;
  • fromRFC1738 instantiates a query string encoded using RFC1738 rules;
  • fromFormData instantiates a query string encoded using application/x-www-form-urlencoded rules;
<?php

use League\Uri\Components\Query;

$query = Query::new('foo=bar&bar=baz%20bar%2A');
//or
$query = Query::fromRFC3986('foo=bar&bar=baz%20bar%2A', '&');
$query->get('bar'); // returns 'baz bar*'

$query = Query::fromRFC1738('foo=bar&bar=baz+bar%2A', '&');
$query->get('bar'); // returns 'baz bar*'

$query = Query::fromFormData('foo=bar&bar=baz+bar*', '&');
$query->get('bar'); // returns 'baz bar*'
copy πŸ“‹

Query separator

The query separator is essential to query manipulation. The Query object provides two (2) simple methods to interact with its separator:

public Query::getSeparator(string $separator): self
public Query::withSeparator(): string
copy πŸ“‹

Query::getSeparator returns the current separator attached to the Query object while Query::withSeparator returns a new Query object with an alternate string separator. Query::withSeparator expects a single argument which is a string separator. If the separator is equal to = an exception will be thrown.

$query    = Query::fromRFC3986('foo=bar&baz=toto');
$newQuery = $query->withSeparator('|');
$newQuery->__toString(); //return foo=bar|baz=toto
copy πŸ“‹

Component representationsΒΆ

In addition to the common methods from the package common API, the following methods are available.

RFC3986 representation

The Query object can return the query encoded using the RFC3986 query component rules

$query = Query::fromRFC1738('foo=bar&bar=baz+bar%2A', '&');
$query->toRFC3986();  //returns 'foo=bar&bar=baz%20bar%2A'
$query->value();     //returns 'foo=bar&bar=baz%20bar%2A'
copy πŸ“‹

If the query is undefined, this method returns null.

Query::toRFC3986() is a alias of Query::value()

RFC1738 representation

The Query object returns the query encoded using the RFC1738 query component rules

$query = Query::fromRFC3986('foo=bar&bar=baz%20bar', '&');
$query->toRFC1738(); // returns 'foo=bar&bar=baz+bar'
$query->jsonSerialize(); //returns 'foo=bar&bar=baz+bar'
copy πŸ“‹

If the query is undefined, this method returns null.

FormData representation

The Query object returns the query encoded using the application/x-www-form-urlencoded query component rules

$query = Query::fromRFC3986('foo=bar&bar=baz%20bar%2A', '&');
$query->toFormData(); // returns 'foo=bar&bar=baz+bar*'
$query->jsonSerialize(); //returns 'foo=bar&bar=baz+bar*'
copy πŸ“‹

If the query is undefined, this method returns null.

Query::jsonSerialize() is an alias of Query::toFormData() to improve interoperability with JavaScript.

Modifying the queryΒΆ

Query::merge

Query::merge returns a new Query object with its data merged.

<?php

public Query::merge($query): Query
copy πŸ“‹

This method expects a single argument which is a string

$query    = Query::fromRFC3986('foo=bar&baz=toto');
$newQuery = $query->merge('foo=jane&r=stone');
$newQuery->__toString(); //return foo=jane&baz=toto&r=stone
// the 'foo' parameter was updated
// the 'r' parameter was added
copy πŸ“‹

Values equal to null or the empty string are merge differently.

$query    = Query::fromRFC3986('foo=bar&baz=toto');
$newQuery = $query->merge('baz=&r');
$newQuery->__toString(); //return foo=bar&baz=&r
// the 'r' parameter was added without any value
// the 'baz' parameter was updated to an empty string and its = sign remains
copy πŸ“‹

Query::append

Query::append returns a new Query object with its data append to it.

public Query::append($query): Query
copy πŸ“‹

This method expects a single argument which is a string, a scalar or an object with the __toString method.

$query    = Query::fromRFC3986('foo=bar&john=doe');
$newQuery = $query->append('foo=baz');
$newQuery->__toString(); //return foo=jane&foo=baz&john=doe
// a new foo parameter is added
copy πŸ“‹

Query::sort

Query::sort returns a Query object with its pairs sorted according to its keys. Sorting is done so that parsing stayed unchanged before and after processing the query.

$query    = Query::fromRFC3986('foo=bar&baz=toto&foo=toto');
$newQuery = $query->sort();
$newQuery->__toString(); //return baz=toto&foo=bar&foo=toto
copy πŸ“‹

since version 7.3.0, the sorting algorithm has been updated to match WHATG group specification

Using the Query as a PHP data transport layerΒΆ

public static Query::fromVariable($params, string $separator = '&'): self
public Query::parameters(): array
public Query::parameter(string $name): mixed
public Query::hasParameter(string ...$name): bool
public Query::withoutNumericIndices(): self
public Query::withoutParameter(...string $offsets): self
copy πŸ“‹

Query::fromVariable replaced the deprecated Query::fromParameters named constructor which was not inconsistent against http_build_query algorithm.

Using PHP data structure to instantiate a new Query object

Historically, the query string has been used as a data transport layer of PHP variables. The fromParams uses PHP own data structure to generate a query string Γ  la http_build_query.

parse_str('foo=bar&bar=baz+bar', $params);

$query = Query::fromVariable($params, '|');
echo $query->value(); // returns 'foo=bar|bar=baz%20bar'
copy πŸ“‹

The $params input can be any argument type supported by http_build_query which means that it can be an iterable or an object with public properties.

If you want a better parsing you can use the QueryString class.

Query::parameters

If you already have an instantiated Query object you can return all the query string deserialized arguments using the Query::parameters method:

$query_string = 'foo.bar=bar&foo_bar=baz';
parse_str($query_string, $out);
var_export($out);
// $out = ["foo_bar" => 'baz'];

$arr = Query::fromRFC3986($query_string)->parameters();
// $arr = ['foo.bar' => 'bar', 'foo_bar' => baz']];
copy πŸ“‹

If you are only interested in a given argument you can access it directly by supplyling the argument name as show below:

$query = Query::fromRFC3986('foo[]=bar&foo[]=y+olo&z=');
$query->parameter('foo');   //return ['bar', 'y+olo']
$query->parameter('gweta'); //return null
copy πŸ“‹

The method returns the value of a specific argument. If the argument does not exist it will return null.

The class can tell whether a parameter or a range of parameters are present using the Query::hasParameter method. The method will take a variadic number of parameter names and will return true only if all the names are present in the query parameter bag.

$query = Query::fromRFC3986('foo[]=bar&foo[]=y+olo&z=');
$query->hasParameter('gweta');        //return false
$query->hasParameter('foo', 'z');     //return true
$query->hasParameter('foo', 'gweta'); //return false
copy πŸ“‹

Query::withoutParameter

If you want to remove PHP’s variable from the query string you can use the Query::withoutParams method as shown below

$query = Query::fromRFC3986('foo[]=bar&foo[]=y+olo&z=');
$new_query = $query->withoutParameter('foo');
$new_query->params('foo'); //return null
echo $new_query->value(); //return 'z='
copy πŸ“‹

This method takes a variadic arguments representing the keys to be removed.

Query::withoutNumericIndices

If your query string is created with http_build_query or the Query::fromParams named constructor chances are that numeric indices have been added by the method.

The Query::withoutNumericIndices removes any numeric index found in the query string as shown below:

$query = Query::fromVariable(['foo' => ['bar', 'baz']]);
echo $query->value(); //return 'foo[0]=bar&foo[1]=baz'
$new_query = $query->withoutNumericIndices();
echo $new_query->value(); //return 'foo[]=bar&foo[]=baz'
//of note both objects returns the same PHP's variables but differs regarding the pairs
$query->parameters(); //return ['foo' => ['bar', 'baz']]
$new_query->parameters(); //return ['foo' => ['bar', 'baz']]
copy πŸ“‹

Using the Query as a collection of query pairsΒΆ

This class mainly represents the query string as a collection of key/value pairs.

public static Query::fromPairs(iterable $pairs, string $separator = '&'): self
public Query::count(): int
public Query::getIterator(): iterable
public Query::pairs(): iterable
public Query::has(string $key): bool
public Query::hasPair(string $key, ?string $value): bool
public Query::get(string $key): ?string
public Query::getAll(string $key): array
public Query::withPair(string $key, $value): self
public Query::withoutDuplicates(): self
public Query::withoutEmptyPairs(): self
public Query::withoutPairByKey(string ...$keys): self
public Query::withoutPairByValue(?string ...$values): self
public Query::withoutPairByKeyValue(string $key, ?string $value): self
public Query::appendTo(string $key, $value): self
copy πŸ“‹

Query::fromPairs

$pairs = QueryString::parse('foo=bar&bar=baz%20bar', '&', PHP_QUERY_RFC3986);
$query = Query::fromPairs($pairs, '|');

echo $query->value(); // returns 'foo=bar|bar=baz%20bar'
copy πŸ“‹

The $pairs input must an iterable which exposes the same structure as QueryString::parse return type structure.

Returns a new Query object from an array or a Traversable object.

  • $pairs : The submitted data must be an array or a Traversable key/value structure similar to the result of Query::parse.
  • $separator : The query string separator used for string representation, by default, equals to &;

Examples

$query =  Query::fromPairs([
    ['foo', 'bar'],
    ['p', 'yolo'],
    ['z', ''],
]);
echo $query; //display 'foo=bar&p=yolo&z='

$query =  Query::fromPairs([
    ['foo', 'bar'],
    ['p', null],
    ['z', ''],
]);
echo $query; //display 'foo=bar&p&z='
copy πŸ“‹

Countable and IteratorAggregate

The class implements PHP’s Countable and IteratorAggregate interfaces. This means that you can count the number of pairs and use the foreach construct to iterate over them.

$query = new Query::fromRFC1738('foo=bar&p=y+olo&z=');
count($query); //return 3
foreach ($query as $pair) {
    //first round 
    // $pair = ['foo', 'bar']
    //second round
    // $pair = ['p', 'y olo']
}
copy πŸ“‹

When looping the key and the value are decoded.

Query::pairs

The Query::pairs method returns an iterator which enable iterating over each pair where the offset represent the pair name while the value represent the pair value.

$query = Query::fromRFC3986('foo=bar&foo=BAZ&p=y+olo&z=');
foreach ($query->pairs() as $name => $value) {
    //first round 
    // $name = 'foo' and $value = 'bar'
    //second round
    // $name = 'foo' and $value = 'BAZ'
}
copy πŸ“‹

The returned iterable contains decoded data.

Query::has and Query::hasPair

Because a query pair value can be null the Query::has method is used to remove the possible Query::get result ambiguity.

$query = Query::fromRFC3986('foo=bar&p&z=');
$query->getPair('foo');   //return 'bar'
$query->getPair('p');     //return null
$query->getPair('gweta'); //return null

$query->has('gweta'); //return false
$query->has('p');     //return true
copy πŸ“‹

Query::has can take a variable list of keys to validate that they are all pesent in the query.

$query = Query::fromRFC3986('foo=bar&p&z=');
$query->has('foo', 'p', 'z');   //return true
$query->has('foo', 'p', 'x');   //return false
copy πŸ“‹

since version 7.3.0

If you are seeking the presence of a specific pair you may include the pair value in your search using Query::hasPair.

$query = Query::fromRFC3986('foo=bar&p&z=');
$query->hasPair('foo', 'p');  //return false
$query->has('foo', 'bar');    //return true
copy πŸ“‹

Query::get

If you are only interested in a given pair you can access it directly using the Query::get method as show below:

$query = Query::fromRFC3986('foo=bar&foo=BAZ&p=y+olo&z=');
$query->get('foo');   //return 'bar'
$query->get('gweta');  //return null
copy πŸ“‹

The method returns the first value of a specific pair key as explained in the WHATWG documentation. If the key does not exist null will be returned.

The returned data are fully decoded.

Query::getAll

This method will return all the value associated with its submitted $name.

$query = Query::fromRFC3986('foo=bar&foo=BAZ&p=y+olo&z=');
$query->getAll('foo');   //return ['bar', 'BAZ']
$query->getAll('gweta');  //return null
copy πŸ“‹

Query::withoutPairByKey, Query::withoutPairByValue, Query::withoutPairByKeyAndValue

since version 7.3.0

Query::withoutPairByKey returns a new Query object with deleted pairs according to their keys. Query::withoutPairByValue does similar but delete the pairs according to their values. Last but not least Query::withoutPairByKeyAndValue does the deletion depending on the presence of the pair key and value.

Query::withoutPairByKey and Query::withoutPairByValue expect a variable list of key or value to be removed as its sole arguments. Query::withoutPairByKeyAndValue on the other hand expect two (2) parameter the pair’s key and value.

$query = Query::fromRFC3986('foo=bar&p=y+olo&z=');
echo $query->withoutPairByKey('foo', 'p')->toString();  //displays 'z='
echo $query->withoutPairByValue('bar')->toString();  //displays 'p=y+olo&z='
echo $query->withoutPairByKeyAndValue('p', 'y+olo')->toString();  //displays 'foo=bar&z='
copy πŸ“‹

Query::withoutEmptyPairs

Query::withoutEmptyPairs returns a new Query object with deleted empty pairs. A pair is considered empty if its key equals the empty string and its value is null.

$query = Query::fromRFC3986('&&=toto&&&&=&');
$newQuery = $query->withoutEmptyPairs();
echo $query; //displays '&&=toto&&&&=&'
echo $newQuery; //displays '=toto&='
copy πŸ“‹