Re: [RFC] [Discussion] Query Parameter Manipulation Support

From: Date: Mon, 16 Mar 2026 21:04:57 +0000
Subject: Re: [RFC] [Discussion] Query Parameter Manipulation Support
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to internals+get-130376@lists.php.net to get a copy of this message
Hi On 3/9/26 21:47, Máté Kocsis wrote:
Same situation as above, but basically the whole section is a big question mark: I couldn't come up with a good enough idea how arrays could be handled efficiently and ergonomically, and in the same time, how to (mostly) conform to WHATWG URL.
The internal storage should not know about arrays. It should represent things as a list of key-value pairs, with arrays only being materialized for the methods that are intended to deal with arrays. Given that the class is generic, matching the WHATWG API 1:1 is not necessary in my opinion.
Some of my problems: - How should $params->get("foo[bar]") behave? Should we try to parse the supplied key as an array and return the nested item if it exists? I suppose, we would internally store the query params as an array (just like how $_GET does), so then we would definitely need parsing in order to be able to return item "bar" stored within array "foo".
->get() does not currently exist within the RFC. The WHATWG get() is equivalent to ->getFirst().
- However, what should a $params->append("foo[bar]") call do when the query param "foo" is already added with a string value? Should it modify the existing item to be an array (but then how?) or we should just add "foo[bar]" to the query params as-is?
It should add foo[bar] as-is. foo and foo[bar] are different keys. The square-bracket syntax is something that is specific to PHP.
- How to recompose list parameters? Ideally, we should not append [] to these parameters (e.g.
The recomposition should be "dumb" and just use the keys as-is. The complex logic should happen in the getter or setter. Only the 'array' getters and setters deal with the PHP-specific square-bracket syntax. To give an example:
    "k=1&k=2"
    [['k', '1'], ['k', '2']]
    getAll('k')   // ['1', '2']
    getArray('k') // UriException: 'k' is not an array.
    "k[]=1&k[]=2"
    [['k[]', '1'], ['k[]', '2']]
    getAll('k')   // []
    getAll('k[]') // ['1', '2']
    getArray('k') // ['1', '2']
    "k[a]=1&k[b]=2"
    [['k[a]', '1'], ['k[b]', '2']]
    getAll('k')    // []
    getAll('k[a]') // ['1']
    getArray('k')  // ['a' => '1', 'b' => '2']
    "k=1&k[]=2"
    [['k', '1'], ['k[]', '2']]
    getAll('k')   // ['1']
    getAll('k[]') // ['2']
    getArray('k') // UriException: 'k' is not an array.
    "k[]=2&k=3";
    [['k[]', '2'], ['k', '3']];
    getAll('k')   // ['3']
    getAll('k[]') // ['2']
    getArray('k') // UriException: 'k' is not an array.
The string should internally be represented something like the arrays and the getters should work as indicated in the comment. And then for the setters:
    // []
    append('k', '1')   // [['k', '1']]
    append('k', '2')   // [['k', '1'], ['k', '2']]
    append('k[]', '3') // [['k', '1'], ['k', '2'], ['k[]', '3']]
    set('k', '4')      // [['k[]', '3'], ['k', '4']]
The setArray() method would then create a number of entries with square brackets added as appropriate (and delete all entries that would be returned by getArray()). The appendArray() method should probably be dropped to avoid issues with "mixing non-array with array keys". So:
    // []
    setArray('k', ['1'])             // [['k[]', '1']]
    setArray('k', ['1', '2'])        // [['k[]', '1'], ['k[]', '2']]
    setArray('k', ['a' => '1', '2']) // [['k[a]', '1'], ['k[0]', '2']]
    append('k', 'x')                 // [
                                     //  ['k[a]', '1'],
                                     //  ['k[0]', '2'],
                                     //  ['k', 'x'],
                                     // ]
    setArray('k', ['1'])             // [['k[]', '1']]
Does that make sense? Best regards Tim Düsterhus

« previous php.internals (#130376) next »