Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new RequestResponseNamedArgs route strategy #3132

Merged
merged 2 commits into from
Jan 14, 2022
Merged

Add new RequestResponseNamedArgs route strategy #3132

merged 2 commits into from
Jan 14, 2022

Conversation

adoy
Copy link
Contributor

@adoy adoy commented Dec 28, 2021

PHP8.0 added support for named parameters https://wiki.php.net/rfc/named_params
This new strategy will map route to parameters name.

$app->get('/{greeting}/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $name, $greeting) {
    $response->getBody()->write("{$greeting} {$name}");
    return $response;
});

@l0gicgate
Copy link
Member

l0gicgate commented Jan 2, 2022

I like this strategy, it sucks that named parameter mapping isn't available in 7.4 though.

@akrabat what do you think of adding this? It's only available for PHP 8.0 which makes me reluctant. Maybe this should be a Slim 5 feature instead.

@adoy
Copy link
Contributor Author

adoy commented Jan 2, 2022

I fixed the CS issue in the test file. Sorry about that.

Also, I agree that it sucks that this is only available for 8.0+ but 7.4 is now in security fixes only.
My personnal input on this is that I think it's better to penalize people who aren't using the latest versions of PHP rather than penalize people who update.
Projects that I create today are using PHP8.1 and Slim4. I could take advantage of this feature today, but if I wait for slim5 I will also have to migrate my code.

@adoy
Copy link
Contributor Author

adoy commented Jan 2, 2022

We could make the basic use case work with some reflection like this :

public function __invoke(
    callable $callable,
    ServerRequestInterface $request,
    ResponseInterface $response,
    array $routeArguments
): ResponseInterface {
    if (PHP_VERSION_ID >= 80000) {
        return $callable($request, $response, ...$routeArguments);
    }

    $args = [ $request, $response ];
    $rf = new ReflectionFunction($callable);
    $parameters = $rf->getParameters();
    $parametersCount = count($parameters);
    $hasVariadic = false;

    for ($i = 2; $i < $parametersCount; $i++) {
        if ($parameters[$i]->isVariadic()) {
            $hasVariadic = true;
            $args = array_merge($args, array_values($routeArguments));
        } else {
            $parameterName = $parameters[$i]->getName();
            if (isset($routeArguments[$parameterName])) {
                $args[] = $routeArguments[$parameterName];
                unset($routeArguments[$parameterName]);
            } elseif ($parameters[$i]->isOptional()) {
                $args[] = $parameters[$i]->getDefaultValue();
            } else {
                throw new \Exception(sprintf('Argument #%d ($%s) not passed', $i, $parameterName));
            }
        }
    }

    if (!$hasVariadic && !empty($routeArguments)) {
        $parameterName = key($routeArguments);
        throw new \Exception(sprintf('Unknown named parameter $%s', $parameterName));
    }

    return $callable(...$args);
}

If you like this (I'm not a fan of it) we would have to discuss :

  • What do we do if the user give a variadic function (it becomes impossible to identify parameters)
  • We would have to make the two exceptions in my exemple consistant with PHP8 (meaning catching PHP8 exceptions and throw custom exceptions to be consistant across all versions).

I don't think it's worth it for a version that will not be supported in less than a year. But I'm just giving the different solutions so that you can take the one that feet the best with your vision.

@coveralls
Copy link

coveralls commented Jan 3, 2022

Coverage Status

Coverage decreased (-0.1%) to 99.901% when pulling 65b1ad4 on adoy:request-response-namedarg-strategy into 6aa522d on slimphp:4.x.

@l0gicgate
Copy link
Member

l0gicgate commented Jan 3, 2022

My personnal input on this is that I think it's better to penalize people who aren't using the latest versions of PHP rather than penalize people who update.

I agree with this wholeheartedly actually.

I'm fine with merging this after some thought.

Can we write a test for the constructor exception please?

@l0gicgate l0gicgate added this to the 4.10.0 milestone Jan 3, 2022
@adoy
Copy link
Contributor Author

adoy commented Jan 3, 2022

Thanks. I added some tests for both 8.0+ and 7.4 but the coverage will still decrease since the coverage run on 8.1.

@l0gicgate
Copy link
Member

@adoy you should be able to override PHP_VERSION_ID. If not, we will have to create a helper function to return current PHP version instead of using the constant directly which we can stub during testing.

@adoy
Copy link
Contributor Author

adoy commented Jan 6, 2022

Just to make sure to understand, isn't it enough to have one test for PHP7.4 to make sure the exception is thrown and other for PHP8.X to make sure the behaviour is correct ? If I create an Helper function, we'll have the same problem to test this function, if I use the constant inside this helper function. Is it only to have the 100% coverage under PHP8.1 ? As for the constant it is a PHP defined constant so I can not overwrite it, and if I create it only in the used namespace, I will only have the oportunity to define it once, so for only one use case.

Thanks for your insight, it will help me find the best way to resolve it :-)

Copy link
Member

@l0gicgate l0gicgate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one little thing then I'll merge this.

I noticed that the coverage went down because the exception thrown test is not being run since we run code coverage on php 8. It's fine though.

Thanks

public function __construct()
{
if (PHP_VERSION_ID < 80000) {
throw new \RuntimeException('Named arguments are only available for PHP >= 8.0.0');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the use RuntimeException; statement on line 16 and remove the \ here please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure !

adoy and others added 2 commits January 14, 2022 00:16
PHP8.0 added support for named parameters https://wiki.php.net/rfc/named_params
This new strategy will map route to parameters name.

```
$app->get('/{greeting}/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $name, $greeting) {
    $response->getBody()->write("{$greeting} {$name}");
    return $response;
});
```
Copy link
Member

@l0gicgate l0gicgate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this contribution!

@l0gicgate l0gicgate merged commit 0c6c36b into slimphp:4.x Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants