On Tue, Dec 16, 2025 at 7:14 PM Juris Evertovskis <[email protected]> wrote:
> On 2025-12-16 09:53, ignace nyamagana butera wrote:
>
> Since we will be dealing with arrays the following rules could be updated
> when parsing the string using PHP behaviour:
>
> - "&a" should be converted to ['a' => null]
>
> Hey Ignace,
>
> In practice valueless arguments like `?debug` are most often "flags" or
> "booleans" and their presence implies truthiness.
>
> Do you think it would be wrong or confusing to have it converted to
> `['debug' => true]`?
>
> I'm worried that `['a' => null]` would not be that handy since both
> `$params['a']` and `isset($params['a'])` would return falsy which would
> likely be opposite to the intended value.
>
> BR,
> Juris
>
Hi Juris,
> Do you think it would be wrong or confusing to have it converted to
`['debug' => true]`?
Yes IMHO it would be wrong because flag parameters or booleans are
converted to ['debug' => 1]
The ['debug' => null] expresses the presence of the name pair and the
absence of value associated with it.
Let's see how it is currently done:
The WHATWG URL living standard does the following:
let url = new URL('https://example.com?debug&foo=bar&debug=');
console.log(
url.searchParams.toString(), //returns debug=&foo=bar&debug='
);
the pair gets converted to ['debug' => '']. The roundtrip does not conserve
the query string as is but all key/pair (tuples) are present.
In PHP you have currently the following behaviour:
example 1
parse_str('debug&foo=bar&debug=', $params);
var_dump($params, http_build_query($params));
//$params ['debug' => '', 'foo' => 'bar']
//after roundtrip you get 'debug=&foo=bar'
example 2
parse_str('debug&foo=bar&debug=1', $params);
var_dump($params, http_build_query($params));
//$params ['debug' => '1', 'foo' => 'bar']
//after roundtrip you get 'debug=1&foo=bar'
So *you lose data and the query data can be randomly sorted*
parse_str convert the first debug into ['debug' => '']
parse_str overwrites the value (This may be a security concern if you need
to hash/validate your query string)
Since IMHO interoperability and security is important you should prefer an
algorithm that preserves the original query.
The proposed solution is already in use for instance in League/Uri or in
Guzzle
echo Uri::withQueryValues(Utils::uriFor('https://example.com'), [
'debug' => null,
'foo' => 'bar',
'baz' => '',
]), PHP_EOL;
// https://example.com?debug&foo=bar&baz=
Because Guzzle uses an associative array, the debug variable can only
appear once but there is a difference using null and the empty string.
This improves interoperability with other languages and you no longer have
data loss or random query re-arrangement.
Last but not least, the Query objects proposed by Màté all expose:
- a `has` method which will always tell if the key is present regardless of
its value an equivalent to array_key_exists.
- provide a way to have the same parameter appear multiple times in the
query string
So IMHO it is an improvement to also allow the distinction between null and
the empty string so we can finally write in PHP
echo (new Uri\Rfc3986\Query())
->append('debug', null)
->append('foo', 'bar')
->append('debug', '')
->toRfc3986String();
// debug&foo=bar&baz=
Best regards,
Ignace