-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
4.x New Dispatcher & Routing Results #2405
Conversation
0704f28
to
e2d52a2
Compare
e2d52a2
to
4ffbf27
Compare
Slim/Router.php
Outdated
@@ -266,11 +263,13 @@ protected function createDispatcher() | |||
|
|||
if ($this->cacheFile) { | |||
$this->dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [ | |||
'dispatcher' => '\\Slim\\Dispatcher', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use the class constant here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This however makes the app not extendable and a user cannot replace the dispatcher
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the Router can be extended and this method overridden. When instantiating the RoutingMiddleware you can pass a router to it which could be the router of your choice. An alternative would be to pass settings to the Router constructor.
Slim/DispatcherResults.php
Outdated
/** | ||
* @var int | ||
* | ||
* Not Found = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about using an Enum for this $routeStatus
or if it's an overhead use constants instead of comments for valid statuses?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably should be a class constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The routeStatus is what comes from Fastroute. We can convert those to constants. Should the constants be on the Dispatcher or Dispatcher results and what should they be named? FOUND, NOT_FOUND, NOT_ALLOWED?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, class constant or Enum will be more explicit so for me both are good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1: FOUND
, NOT_FOUND
, NOT_ALLOWED
sounds good to me.
2: You use them in DispatcherResults
so it may be the right place (imho use an Enum and inject it is the best approach)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, no need to make changes. This PR extends FastRoute\Dispatcher\GroupCountBased class which implements FastRoute\Dispatcher interface which has those constants. They can be used as Slim\Dispatcher::FOUND, Slim\Dispatcher::NOT_FOUND, Slim\Dispatcher::NOT_ALLOWED
I will amend the comment in DispatcherResults to reference where those integers come from.
Slim/DispatcherResults.php
Outdated
*/ | ||
public function getRouteArguments($urlDecode = true) | ||
{ | ||
if ($urlDecode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid nested statement:
if (!$urlDecode) {
return $this->routeArguments;
}
$routeArguments = [];
foreach ($this->routeArguments as $key => $value) {
$routeArguments[$key] = urldecode($value);
}
return $routeArguments;
} | ||
|
||
// For HEAD requests, attempt fallback to GET | ||
if ($httpMethod === 'HEAD') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wdyt about adding https://github.com/php-fig/http-message-util
to avoid hardcoded HTTP methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HTTP Methods don't change. I dont think we need to include a package for constants, we aren't javascript.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always include it in for descriptive HTTP status codes and it contains have HTTP methods too.
Seen that is a FIG package I'm ok with it.
But I get your point of view so it's not a big deal 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If anywhere, this should be on the Slim\Http package.
tests/RouterTest.php
Outdated
} | ||
|
||
public function testSetDispatcher() | ||
{ | ||
$this->router->setDispatcher(\FastRoute\simpleDispatcher(function ($r) { | ||
$r->addRoute('GET', '/', function () { | ||
}); | ||
})); | ||
}, ['dispatcher' => '\\Slim\\Dispatcher'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Dispatcher::class
instead of hardcoded namespace, if it will change we have to change it in a lot of places
tests/RouterTest.php
Outdated
@@ -362,7 +364,7 @@ public function testRouteCacheFileCanBeDispatched() | |||
$method->setAccessible(true); | |||
|
|||
$dispatcher = $method->invoke($this->router); | |||
$this->assertInstanceOf('\FastRoute\Dispatcher', $dispatcher); | |||
$this->assertInstanceOf('\Slim\Dispatcher', $dispatcher); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@l0gicgate Here namespace too 😄
tests/RouterTest.php
Outdated
$class = new \ReflectionClass($this->router); | ||
$prop = $class->getProperty('dispatcher'); | ||
$prop->setAccessible(true); | ||
$this->assertInstanceOf('\FastRoute\Dispatcher', $prop->getValue($this->router)); | ||
$this->assertInstanceOf('\Slim\Dispatcher', $prop->getValue($this->router)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@l0gicgate Here namespace too 😄
tests/RouterTest.php
Outdated
@@ -205,19 +207,19 @@ public function testCreateDispatcher() | |||
$class = new \ReflectionClass($this->router); | |||
$method = $class->getMethod('createDispatcher'); | |||
$method->setAccessible(true); | |||
$this->assertInstanceOf('\FastRoute\Dispatcher', $method->invoke($this->router)); | |||
$this->assertInstanceOf('\Slim\Dispatcher', $method->invoke($this->router)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@l0gicgate Here namespace too 😄
tests/DispatcherTest.php
Outdated
|
||
protected function getDataGeneratorClass() | ||
{ | ||
return 'FastRoute\\DataGenerator\\GroupCountBased'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we including it as dependency?
We may use namespace here too instead of hardcoded 😄
tests/DispatcherTest.php
Outdated
{ | ||
protected function getDispatcherClass() | ||
{ | ||
return 'Slim\\Dispatcher'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@l0gicgate Here namespace too 😄
$routeArguments[$k] = urldecode($v); | ||
} | ||
switch ($routeStatus) { | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default
should be last in a switch statement. Arguably, it should throw an Exception as it should never happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What kind of exception would you like to be thrown here? Just a regular exception or RuntimeException
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Runtime is fine as it should never happen :)
Slim/Dispatcher.php
Outdated
} | ||
} | ||
|
||
return $allowedMethods; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be caching the list of allowed methods in an instance variable keyed by the function's parameters? It appears that we currently have to do this same work twice when we're in a method not allowed scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per one of our previous discussions, this avoids the initial performance hit of looping through every single route. It is being returned on demand instead of initially doing it every time we dispatch. This is also the way Fastroute does it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that.
Given getAllowedMethods()
has been called, why don't we keep a copy of the calculated $allowedMethods
in a local member variable, so that we don' have to do all the work again if getAllowedMethods()
is called again with exactly the same parameters?
From what I can tell if we're in a Method Not Allowed situation, we call getAllowedMethods()
on line 68 of Dispatcher.php
and then again in line 83 of RoutingMiddleware.php, so it appears to me that we're doing the same work twice.
Slim/DispatcherResults.php
Outdated
* @param bool $urlDecode | ||
* @return array | ||
*/ | ||
public function getRouteArguments($urlDecode = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we not want to url decode the route arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what is currently being done https://github.com/slimphp/Slim/blob/4.x/Slim/Middleware/RoutingMiddleware.php#L66
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is that currently in Slim 3 it's not optional. Why is the choice a method parameter here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per our discussion on slack, we're leaving this as is.
Slim/Router.php
Outdated
* @return array | ||
* | ||
* @link https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php | ||
* @return DispatcherResults |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add
$uri = rawurldecode($uri);
before calling the dispatcher's dispatch()
in this method. Not sure if it should be a separate PR or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This commit cc0c0b3 applies the requested change.
@l0gicgate btw before merging could you please squash all your commits? |
@damianopetrungaro I'm not the one merging, @akrabat will take care of that! |
You can do it also on your local branch: |
Fix rebase merge conflict
Add url decoding functionality to getRouteArguments() method
Fix copyright year
Reference $routeStatus constant origin and fix nested conditional Use class constant instead of string reference Use class constant instead of string reference Use class constant instead of string reference Fix switch default case in RoutingMiddleware
Add allowed methods caching in Dispatcher Add test case for routes with international characters Add test case for international characters in route arguments
99144ab
to
9c50cb9
Compare
FWiW, I am not a fan of rebasing to clean history. Rebasing to remove a commit that's a typo fix is fine, but I personally prefer to see the route that was taken to get to where we are now. |
Slim/Dispatcher.php
Outdated
*/ | ||
public function getAllowedMethods($httpMethod, $uri) | ||
{ | ||
if ($this->allowedMethods !== null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will give the wrong answer if I do this:
$methods1 = $o->getAllowedMethods('POST', '/foo');
$methods2 = $o->getAllowedMethods('GET', '/bar');
At least, I think that $methods2
will be the allowed methods for /foo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, I made a mistake. I fixed this logic in latest commit. Good find!
Great work @l0gicgate ! |
As per #2399
As discussed on Slack, there are a few issues with the way
FastRoute\Dispatcher
returns information after a route has been dispatched.Issues
FastRoute\Dispatcher::dispatch()
returns an array with an inconsistent structureChanges
routeInfo
now deprecated and replaced withroutingResults
RoutingResults
is an immutable object instantiated by the Slim Dispatcher which now offers a way to get all a route's allowed methods which was not accessible before viaFastRoute\GroupCountBased\Dispatcher
Usage
Status