On Sun, Dec 21, 2025 at 4:51 PM ignace nyamagana butera <[email protected]> wrote:
> On Sun, Dec 21, 2025 at 1:13 PM Máté Kocsis <[email protected]> > wrote: > >> Hi Ignace, >> >> >>> When I talk about data mangling I am talking about this >> >> >>> parse_str('foo.bar=baz', $params); >>> var_dump($params); //returns ['foo_bar' => 'baz'] >> >> >> Sure! I just wanted to point it out that name mangling will still be >> present due to arrays, and this will have some disadvantages, >> namely that adding params and retrieving them won't be symmetric: >> >> $params = new Uri\Rfc3986\UriQueryParams() >> ->append("foo", [2 => "bar", 4 => "baz"]); >> >> var_dump($params->getFirst("foo")); // NULL >> >> One cannot be sure if a parameter that was added can really be retrieved >> later via a get*() method. >> >> Another edge cases: >> >> $params = new Uri\Rfc3986\UriQueryParams() >> ->append("foo", ["bar", "baz"]) // Value is >> a list, so "foo" is added without brackets >> ->append("foo", [2 => "qux", 4 => "quux"]); // Value is an >> array, so "foo" is added with brackets >> >> var_dump($params->toRfc3986String()); // >> foo=bar&foo=baz&foo%5B2%5D=qux&foo%5B4%5D=quux >> >> var_dump($params->getLast("foo")) // Should it be "baz" >> or "quux"? >> var_dump($params->getAll("foo")) // Should it only >> include the params with name "foo", or also "foo[]"? >> >> And of course this behavior also makes the implementation incompatible >> with the WHATWG URL specification: although I do think this part of >> the specification is way too underspecified and vague, so URLSearchParams >> doesn't seem well-usable in practice... >> >> So an idea that I'm now pondering about is to keep the append() and set() >> methods compatible with WHATWG: and they would only support >> scalar values, therefore the param name wasn't mangled. And an extra >> appendArray() and setArray() method could be added that would possible >> mangle the param names, and they would only support passing arrays. This >> solution would hopefully result in a slightly less surprising >> behavior: one could immediately know if a previously added parameter is >> really retrievable (when append() or set() was used), or extra >> checks may be needed (when using appendArray() or setArray()). >> >> >>> This to me should yield the same result as ->append('foo', null); as >>> the array construct is only indicative of a repeating parameter name >>> if there is no repeat then it means no data is attached to the name. >> >> >> Alright, that seems the least problematic solution indeed, and >> http_build_query() also represents empty arrays just like null values >> (omitting them). >> >> So in your implementation it would mean: >>> >>> allowed type: null, int, float, string, boolean, and Backed Enum (to >>> minic json_encode and PHP8.4+ behaviour) >>> arrays with values containing valid allowed type or array. are also >>> supported to allow complex type support. >>> >>> Any other type (object, resource, Pure Enum) are disallowed they should >>> throw a TypeError >>> >> >> +1 >> >> >>> Maybe in the future scope of this RFC or in this RFC depending on how >>> you scope the RFC you may introduce an Interface which will allow >>> serializing objects using a representation that >>> follows the described rules above. Similar to what the >>> JsonSerializable interface is for json_encode. >>> >> >> Hm, good idea! I'm not particularly interested in this feature, but I >> agree it's a good way to add support for objects. >> >> Last but not Last, all this SHOULD not affect how http_buid_query works. >>> The function should never have been modified IMHO so it should be left >>> untouched by all this except if we allow it >>> to opt-in the behaviour once the interface is approved and added to PHP. >>> >> >> +1 >> >> Regards, >> Máté >> >> > Hi Máté, > > And an extra appendArray() and setArray() method could be added that >> would possible >> mangle the param names, and they would only support passing arrays. This >> solution would hopefully result in a slightly less surprising >> behavior: one could immediately know if a previously added parameter is >> really retrievable (when append() or set() was used), or extra >> checks may be needed (when using appendArray() or setArray()). > > > I believe adding the appendArray and setArray is the way forward as the > bracket addition and thus mangling is really a PHP specificity that we MUST > keep to avoid hard BC. > I would even go a step further and add a getArray and hasArray methods > which will lead to the following API > > $params = (new Uri\Rfc3986\UriQueryParams()) > ->append("foo", ["bar", "baz"]) // Value is a list, so "foo" is > added without brackets > ->appendArray("foo", ["qux", "quux"]); // Value is a list, using PHP > serialization "foo" is added with brackets > > var_dump($params->toRfc3986String()); // > foo=bar&foo=baz&foo%5B0%5D=qux&foo%5B1%5D=quux > > $params->hasArray('foo'); //returns true > $params->getArray("foo"); //returns ["qux", "quux"] > > $params->has('foo'); //returns true > $params->getFirst("foo"); //returns "bar" > $params->getLast("foo"); //returns "baz" > $params->getAll('foo'); //returns ["bar", "baz"] > > Hope this makes sense > > Regards, > Ignace > > Hi Màté, I have been playing around your Query Param API and I have a couple of questions: Question 1) While I am not a proponent of the addition of the getQueryParams on both classes even though I know the method exists in the WHATWG URL spec I find strange is that the method may return null. To me this makes for an awkward API where the user will always have to add some conditional checks before using the method returned value. Why can't this be true ? $url = Uri\Rfc3986\Uri::parse('https://www.example.com/path/to/whatever'); $url->getQueryParams(); // should return a empty UriQueryParams instance $url = Uri\Rfc3986\Uri::parse('https://www.example.com/path/to/whatever?'); $url->getQueryParams(); // should return UriQueryParams with a pair // represented like this ['' => null] or like this ['', null] This IMHO should also be the case for the UrlQueryParams instance Question 1-bis) I prefer having some extra named constructors on the UrlQueryParams instead of having a getter on the Uri/Url classes. This fully decoupled the Ur(i|l)QueryParams from the Uri/Url classes and let the user opt-in the new API if needed. In case of errors/bugs etc... only the QueryParams cointainer bags would be affected ... not the Url/Uri classes. Question 2) I see you have - UriQueryParams::fromArray, - UriQueryParams::list, If I read it correctly, this returns 2 array representations of the query ? My question is shouldn't we have either a fromList named constructor and/or a toArray which return both distinctive forms ? This might confused the developer who will have a hard time understand which form is what and when to use it and it which one in which context can be used to instantiate a new instance ? Question 3) I wanted to know how the following code will be processed ? $query = 'a[]=foo&a[]=bar&a=qux'; parse_str($query, $result); $result['a']; //returns "qux" As seen in the example with parse_str the full array notation is overwritten and can not be used/accessed Will the getArray API still be able to access the array data or will it act like parse_str and skip the array notation ? Best regards, Ignace
