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
[Routing] Optimised dumped router matcher, prevent unneeded function calls. #21755
Changes from 2 commits
ea8c55a
0d69cfe
0425f33
56f2323
ef6ad3a
44271e0
e4a9e1e
2e180c6
9d92c57
f56cf9a
bb68154
4ab821b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,8 +105,10 @@ public function match(\$pathinfo) | |
{ | ||
\$allow = array(); | ||
\$pathinfo = rawurldecode(\$pathinfo); | ||
\$trimmedPathinfo = rtrim(\$pathinfo, '/'); | ||
\$context = \$this->context; | ||
\$request = \$this->request; | ||
\$requestMethod = \$context->getMethod(); | ||
|
||
$code | ||
|
||
|
@@ -133,7 +135,7 @@ private function compileRoutes(RouteCollection $routes, $supportsRedirections) | |
foreach ($groups as $collection) { | ||
if (null !== $regex = $collection->getAttribute('host_regex')) { | ||
if (!$fetchedHost) { | ||
$code .= " \$host = \$this->context->getHost();\n\n"; | ||
$code .= " \$host = \$context->getHost();\n\n"; | ||
$fetchedHost = true; | ||
} | ||
|
||
|
@@ -227,7 +229,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren | |
|
||
if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.(substr($regex, -1) === 'u' ? 'u' : ''), $regex, $m)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This removed N trim calls. |
||
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') { | ||
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); | ||
$conditions[] = sprintf('$trimmedPathinfo === %s', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); | ||
$hasTrailingSlash = true; | ||
} else { | ||
$conditions[] = sprintf('$pathinfo === %s', var_export(str_replace('\\', '', $m['url']), true)); | ||
|
@@ -266,7 +268,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren | |
if ($methods) { | ||
if (1 === count($methods)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This removes N function calls. |
||
$code .= <<<EOF | ||
if (\$this->context->getMethod() != '$methods[0]') { | ||
if (\$requestMethod != '$methods[0]') { | ||
\$allow[] = '$methods[0]'; | ||
goto $gotoname; | ||
} | ||
|
@@ -276,7 +278,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren | |
} else { | ||
$methods = implode("', '", $methods); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This removes N function calls. |
||
$code .= <<<EOF | ||
if (!in_array(\$this->context->getMethod(), array('$methods'))) { | ||
if (!in_array(\$requestMethod, array('$methods'))) { | ||
\$allow = array_merge(\$allow, array('$methods')); | ||
goto $gotoname; | ||
} | ||
|
@@ -303,7 +305,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren | |
$schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); | ||
$code .= <<<EOF | ||
\$requiredSchemes = $schemes; | ||
if (!isset(\$requiredSchemes[\$this->context->getScheme()])) { | ||
if (!isset(\$requiredSchemes[\$context->getScheme()])) { | ||
return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes)); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,8 +24,10 @@ public function match($pathinfo) | |
{ | ||
$allow = array(); | ||
$pathinfo = rawurldecode($pathinfo); | ||
$trimmedPathinfo = rtrim($pathinfo, '/'); | ||
$context = $this->context; | ||
$request = $this->request; | ||
$requestMethod = $context->getMethod(); | ||
|
||
// foo | ||
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) { | ||
|
@@ -35,7 +37,7 @@ public function match($pathinfo) | |
if (0 === strpos($pathinfo, '/bar')) { | ||
// bar | ||
if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) { | ||
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { | ||
if (!in_array($requestMethod, array('GET', 'HEAD'))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading this line makes me think that we could even have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't we then loose the ability to distinguish the request when there's only a HEAD route defined? I see in the there's a case for adding HEAD to the accepted methods when it's a GET request, but not the other way around. So if we only have a HEAD registered, we wouldn't be able to match that anymore, right? This would only be the case if you want to support defining separate HEAD handlers, I don't know if that's currently the case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll just test this out real quick. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tested this out. We'd loose the ability to define HEAD routes when we make that additional optimisation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I wasn't clear enough. My idea was to create a specific variable (different from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That shaved off a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've implemented it in such a way that it keeps current behaviour, which also became more clear to me. If there's a HEAD registered after a GET for the same route, the first route will be matched. So now there is also a variable for the special "GET is also HEAD" case. The compiled route needs to remain aware whether there was actually a HEAD clause in there initially so it uses the right request method variable, the not "special" one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I begin to wonder though, the code generating this, and executing it, become less easy to understand. Since the boost is so enormously tiny of that last bit I suggest to consider reverting that one commit and keep the rest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added the tests for the other optimisation just in case. It's 03:00 here now, so bed time. Have a good one 👍 |
||
$allow = array_merge($allow, array('GET', 'HEAD')); | ||
goto not_bar; | ||
} | ||
|
@@ -46,7 +48,7 @@ public function match($pathinfo) | |
|
||
// barhead | ||
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) { | ||
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { | ||
if (!in_array($requestMethod, array('GET', 'HEAD'))) { | ||
$allow = array_merge($allow, array('GET', 'HEAD')); | ||
goto not_barhead; | ||
} | ||
|
@@ -70,7 +72,7 @@ public function match($pathinfo) | |
} | ||
|
||
// baz3 | ||
if (rtrim($pathinfo, '/') === '/test/baz3') { | ||
if ($trimmedPathinfo === '/test/baz3') { | ||
if (substr($pathinfo, -1) !== '/') { | ||
return $this->redirect($pathinfo.'/', 'baz3'); | ||
} | ||
|
@@ -91,7 +93,7 @@ public function match($pathinfo) | |
|
||
// baz5 | ||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) { | ||
if ($this->context->getMethod() != 'POST') { | ||
if ($requestMethod != 'POST') { | ||
$allow[] = 'POST'; | ||
goto not_baz5; | ||
} | ||
|
@@ -102,7 +104,7 @@ public function match($pathinfo) | |
|
||
// baz.baz6 | ||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) { | ||
if ($this->context->getMethod() != 'PUT') { | ||
if ($requestMethod != 'PUT') { | ||
$allow[] = 'PUT'; | ||
goto not_bazbaz6; | ||
} | ||
|
@@ -174,7 +176,7 @@ public function match($pathinfo) | |
} | ||
|
||
// hey | ||
if (rtrim($pathinfo, '/') === '/multi/hey') { | ||
if ($trimmedPathinfo === '/multi/hey') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (substr($pathinfo, -1) !== '/') { | ||
return $this->redirect($pathinfo.'/', 'hey'); | ||
} | ||
|
@@ -207,7 +209,7 @@ public function match($pathinfo) | |
|
||
} | ||
|
||
$host = $this->context->getHost(); | ||
$host = $context->getHost(); | ||
|
||
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { | ||
// route1 | ||
|
@@ -322,7 +324,7 @@ public function match($pathinfo) | |
// secure | ||
if ($pathinfo === '/secure') { | ||
$requiredSchemes = array ( 'https' => 0,); | ||
if (!isset($requiredSchemes[$this->context->getScheme()])) { | ||
if (!isset($requiredSchemes[$context->getScheme()])) { | ||
return $this->redirect($pathinfo, 'secure', key($requiredSchemes)); | ||
} | ||
|
||
|
@@ -332,7 +334,7 @@ public function match($pathinfo) | |
// nonsecure | ||
if ($pathinfo === '/nonsecure') { | ||
$requiredSchemes = array ( 'http' => 0,); | ||
if (!isset($requiredSchemes[$this->context->getScheme()])) { | ||
if (!isset($requiredSchemes[$context->getScheme()])) { | ||
return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes)); | ||
} | ||
|
||
|
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 removes a reference lookup, probably hardly any impact, but in optimised code, every cycle counts, right?