[Routing] fix trailing slash redirection

This commit is contained in:
Nicolas Grekas 2018-11-29 12:01:52 +01:00
parent bfe2357ffa
commit fbaba23023
14 changed files with 143 additions and 38 deletions

View File

@ -550,9 +550,15 @@ EOF;
private function compileSwitchDefault(bool $hasVars, bool $matchHost): string private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
{ {
$code = sprintf(" $code = sprintf("
if ('/' !== \$pathinfo && \$hasTrailingSlash !== ('/' === \$pathinfo[-1])) { if ('/' !== \$pathinfo) {
%s; if (!\$hasTrailingSlash && '/' === \$pathinfo[-1]%s) {
%s;
}
if (\$hasTrailingSlash && '/' !== \$pathinfo[-1]) {
%2\$s;
}
}\n", }\n",
$hasVars ? ' && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n[\'MARK\']' : '',
$this->supportsRedirections ? 'return null' : 'break' $this->supportsRedirections ? 'return null' : 'break'
); );

View File

@ -160,8 +160,13 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
continue; continue;
} }
if ($supportsTrailingSlash && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ($supportsTrailingSlash) {
return; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1))) {
return;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return;
}
} }
$hostMatches = array(); $hostMatches = array();

View File

@ -54,8 +54,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
if ($requiredHost) { if ($requiredHost) {
@ -232,8 +237,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -2793,8 +2793,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -118,8 +118,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -63,8 +63,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -91,8 +91,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
if ($requiredHost) { if ($requiredHost) {
@ -269,8 +274,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -46,8 +46,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
@ -82,8 +87,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -66,8 +66,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);

View File

@ -83,8 +83,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
@ -121,8 +126,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -44,8 +44,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
@ -98,8 +103,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -79,8 +79,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1]) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
@ -133,8 +138,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
return null; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
return null;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
return null;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -51,8 +51,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m];
if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { if ('/' !== $pathinfo) {
break; if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) {
break;
}
if ($hasTrailingSlash && '/' !== $pathinfo[-1]) {
break;
}
} }
foreach ($vars as $i => $v) { foreach ($vars as $i => $v) {

View File

@ -680,6 +680,15 @@ class UrlMatcherTest extends TestCase
$this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']); $this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']);
} }
public function testSlashVariant()
{
$coll = new RouteCollection();
$coll->add('a', new Route('/foo/{bar}', array(), array('bar' => '.*')));
$matcher = $this->getUrlMatcher($coll);
$this->assertEquals('a', $matcher->match('/foo/')['_route']);
}
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
{ {
return new UrlMatcher($routes, $context ?: new RequestContext()); return new UrlMatcher($routes, $context ?: new RequestContext());