Slim icon indicating copy to clipboard operation
Slim copied to clipboard

RequestResponseNamedArgs and Typed Parameters

Open BusterNeece opened this issue 3 years ago • 4 comments

I was in the process of migrating my web application to use the Slim RequestResponseNamedArgs controller invoker in favor of the previous PHP-DI/Invoker-based one I was using, and I had expected that the transition would be a fairly straightforward one, but I'm running into one big issue: if you type a parameter in a controller action, for example as an int, it can never coerce into that value:

<?
$app->get('/test', function($request, $response, int $id) {
    // This will fail with RequestResponseNamedArgs
});

The reason this fails is because, at least per my own research, there's no way to specify that a route parameter should be cast as an integer, even if it only accepts numeric values per its FastRoute config. Because declare(strict_types=1) is set on the RequestResponseNamedArgs class, it won't coerce the always-string values for parameters to fit the typed argument in the action.

I'm not sure what the elegant solution to this is. You could remove the strict typing from that single class, since any type strictness originates from the calling file and this would allow type coercion...or somehow let FastRoute know that it should be parsing some parameters as integers.

I'm not even sure that it's something you'd want to fix on the framework side at all, instead choosing to say that every passed parameter will be a string because, well, URLs are strings. That's certainly what my immediate resolution will be, just to re-type all of those action arguments as strings and parse them down the line.

BusterNeece avatar May 24 '22 01:05 BusterNeece

~I think that would go beyond the scope of Slim.~

By default, Slim controllers have a strict signature: $request, $response, array $args. The PHP-DI bridge offers an alternative, for strictly typed parameters such as the route placeholders.

But generally, this is also quite easy to write it like this (without RequestResponseNamedArgs):

$app->get('/test/{id}', function($request, $response, array $args) {
    $id = (int)$args['id'];
    // ...
});

odan avatar May 24 '22 09:05 odan

@odan The RequestResponseNamedArgs controller invocation strategy is provided by, and part of, the Slim repo. While it isn't the default, it's certainly within the scope of Slim.

BusterNeece avatar May 24 '22 13:05 BusterNeece

That's right, obviously this feature was added in version 4.1. https://github.com/slimphp/Slim/pull/3132 I was not aware of that, and the reference is not yet included in the documentation.

I guess this specific feature (typed parameters) is not supported at the moment, because the ... operator in combination with declare(strict_types=1); requires the correct type. https://3v4l.org/SFgmi

~I guess the easiest solution would be to remove declare(strict_types=1); in your controller files and let PHP do the type casting.~ The more complex solution would be to add some kind of reflection based type resolver and caster to the RequestResponseNamedArgs class.

odan avatar May 27 '22 10:05 odan

@odan Like I mentioned in the original issue, the type strictness of a call is determined by the calling file, not the called file, so removing strictness on my controller classes wouldn't affect anything.

BusterNeece avatar May 27 '22 21:05 BusterNeece