Hi
On 2026-03-01 23:31, Máté Kocsis wrote:
As I mentioned in a previous email of mine (
https://externals.io/message/129486#130077),
I recently separated the query parameter handling sub-proposal from
https://wiki.php.net/rfc/uri_followup into its own RFC because it was
way
too complex.
Therefore I'm officially opening its discussion.
I've given the RFC a fresh read from scratch after your email
https://news-web.php.net/php.internals/132810. I'm replying to the top
of the thread to reduce the nesting, and because I'm not replying to
anyone or anything in particular. Some collected thoughts roughly in
order they appear in the RFC:
1. I don't think `QueryParamParsingOptions::$maxQueryStringLength` is
necessary or useful to have. Checking the input length is easy to do
manually (it's just a `strlen()` call) and I don't see a practical way
to abuse the API with large input lengths alone: The API will not “blow
up” inputs into considerably larger data structures, the input strings
are mostly just “copied” over, not too different from e.g.
`str_replace()`.
2. The `$maxParamCount` on the other hand makes more sense to me, but
even for that the number of “entries” will be at most the input length
(for `&&&`).
3. If `$maxQueryStringLength` goes away, the `QueryParamParsingOptions`
would be a single property object. Maybe the param count limit should
become a regular parameter then, since I can't think of any other useful
parse-time options.
4. For the `QueryParamBuildingOptions` I don't think they should be
passed as parameter to the parsing functions (and thus not be stored as
an object property). Instead they should be passed to the `->to*()`
functions just when they are needed. That keeps the QueryParams class’
state focused on the actual parameters.
Edit after getting further down the RFC: This choice is explained
further below, because it immediately affects the getters. This makes
sense, but I still believe this is a less than ideal API design. But I
don't have better proposals other than “force the user to make a
decision and only allow passing in `string`”. For `int` the implicit
conversion to string would just work and making an explicit choice for
`bool` is easy enough.
5. Is having a separate `parseRfc3986()` useful when RFC 3986 doesn't
specify the handling of query parameters and just delegates to RFC 1866
instead? A big benefit of the URI extension for me is that it clearly
references the relevant standards in its naming and strictly implements
them, making it easy to discover and allowing the use of primary
references to understand what they are supposed to do. The
`parseRfc3986()` method would muddy this straight-forward standard’s
compliant implementation, particularly since it is defined to accept
malformed inputs such as `#foo=bar`, which I would find unexpected from
a method referencing RFC 3986 in its name.
6. The Uri::getQueryParams() method on the other hand makes sense to me.
It can use RFC 1866 as an implementation detail after performing the
correct percent decoding.
7. I cannot describe an actual issue, but I find accepting invalid
percent-encoding sequences to be dangerous. It would definitely be
unexpected that `%6f=x' would be `o=x`, but `%6g=y` would be `%6g=y`
(which would then become `%256g=y`).
8. The naming of `hasValue()` sounds like it would check if there is a
`=` sign for the given key somewhere, not that it would check that there
is a specific key-value pair. I'm also not sure if it is necessary.
Duplicated keys are comparatively rare and for the use cases where this
is a thing, `in_array()` on the result of `getAll()` would just work (or
`array_any()` for more complex checks).
9. I'm not sure I like the name `->list()`. Technically it's also
redundant with `iterator_to_array($queryParams)`, but having a dedicated
method probably makes sense nevertheless.
10. Would it make sense to rename `parseWhatWg()` to
`parseUrlSearchParams()` or similar to make it clear that this
constructor implements the URLSearchParams specification. I'm asking due
to the section that mentioned how `$whatWgUrl->getQuery()` is not the
correct value to pass there (due to the questionmark handling). Renaming
this method would also make sense when removing the RFC 3986 parser (as
suggested in (5)).
11. Bullet point 8 also applies to `deleteValue()`. Is there an actual
use case for this method?
12. Is having the `withArray()` method useful? It does the same as
`QueryParams::fromArray()`, no? It feels confusing to have a “replace
everything” method when one could just create something entirely new.
Technically one could even call `$queryParams->fromArray()`, since
calling static methods on instances is legal.
13. “doesn't start with a “with” suffix” - that should read “prefix”.
Prefix is “start with”, suffix is “end with”.
14. The “Backward Incompatible Changes” section should mention that
adding a new class is technically backwards incompatible, but an allowed
break according to policy and that the Uri namespace is reserved for
ext/uri since 8.5.
That's it. It looks like a long list of items, but most of them are
minor concerns and suggestion. Overall I'm super happy with how the RFC
turned out and I believe that giving it the proper attention as a
dedicated RFC was the right choice.
Best regards
Tim Düsterhus