Hello Tim et All,
I managed to implement a considerable part of the proposal efficiently, as
I planned. However, I came to a different conclusion
regarding the "Array API" (https://wiki.php.net/rfc/query_params#array_api)
than what I originally wrote:
First and foremost, I wasn't happy that the Array API would try to mimic
native array handling. For example, QueryParams::getArray("foo[bar][baz]")
would try to parse the supplied array dimensions and then find index "baz"
of index "bar" of array "foo". QueryParams::appendArray() and
QueryParams::setArray() would need a similar behavior. I wasn't keen on
reimplementing array operations within query params. In order to solve
this Gordian knot, I want to simplify the Array API to only two (or maybe
three) methods:
- QueryParams::fromArray(): Creates a new QueryParams object from an array
- QueryParams::withArray(): Does the same as above, but from an existing
instance (I'm not 100% sure about this method yet)
- QueryParams::toArray(): Creates an array from the QueryParams instance
If someone wants to do any kind of array modifications, then they can do it
by retrieving the query params array first via toArray(), change what's
needed
by using PHP's native array manipulation functionalities, and finally,
convert the.array back to a QueryParams instance. I think this behavior is
simple, clear,
and also extensible later, if we learn about a better approach...
Besides, I also realized that supporting some kind of configuration will
also be necessary mostly for security reasons. These are the options
I'm considering to add:
- parsingMaxQueryStringLength: the maximum length of the input query string
that can be parsed by the parse*() methods
- parsingMaxParamCount: The maximum number of parameters that the input
query string can contain when calling any parse*() methods (similar
to the "max_input_vars" php.ini option).
- parsingMaxNestingLevel: The maximum nesting level of arrays that can be
parsed when calling any parse*() methods (similar
to the "max_input_nesting_level" php.ini option)
I figured that it might be useful to control how some specific type of
query parameter values are represented when adding a new query
parameter via calling e.g. QueryParams::append("param", $value).
- boolean values: by default, true would be represented as "1" and false
would be represented as "0"
- null values: there are three ways to handle them: 1) omitting these; 2)
adding them without a "=", containing only the param name (e.g. "?foo");
3) and converting the null value to an empty string (e.g. "?foo=")
I'm not really sure how useful these options are, so I'm curious to get
feedback about them. It's also an option to only accept string or null
values by the manipulator methods, but I think it would provide a better UX
to support all valid types (e.g. an int is a valid one, but an object
isn't).
It's also a question how and where these configs should be passed.
Currently, I group them together in a dedicated QueryParamOptions object
which has the following signature:
final readonly class QueryParamOptions
{
public function __construct(
public int $parsingMaxQueryStringLength = 10000,
public int $parsingMaxParamCount = 1000,
public int $parsingMaxNestingLevel = 64,
public string $trueValue = "0",
public string $falseValue = "0",
public bool $useNullAsEmptyString = false
) {}}
However, the properties prefixed by "parsing" are only applicable during
parsing, so maybe they should be passed separately to the parse*() methods?
What are your thoughts?
Regards,
Máté