diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index 4cb1c7429b..c33b7d06c5 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -550,9 +550,15 @@ EOF; private function compileSwitchDefault(bool $hasVars, bool $matchHost): string { $code = sprintf(" - if ('/' !== \$pathinfo && \$hasTrailingSlash !== ('/' === \$pathinfo[-1])) { - %s; + if ('/' !== \$pathinfo) { + if (!\$hasTrailingSlash && '/' === \$pathinfo[-1]%s) { + %s; + } + if (\$hasTrailingSlash && '/' !== \$pathinfo[-1]) { + %2\$s; + } }\n", + $hasVars ? ' && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n[\'MARK\']' : '', $this->supportsRedirections ? 'return null' : 'break' ); diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index f2b353dfeb..13b231bf96 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -160,8 +160,13 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface continue; } - if ($supportsTrailingSlash && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return; + if ($supportsTrailingSlash) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1))) { + return; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return; + } } $hostMatches = array(); diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php index 531d1ecaba..472b96a35c 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -54,8 +54,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } if ($requiredHost) { @@ -232,8 +237,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php index eee7e080d8..956a8bad68 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php @@ -2793,8 +2793,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php index 8f3f0599f2..3cc68ed993 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php @@ -118,8 +118,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php index 406e13336a..8b446a2fc2 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher12.php @@ -63,8 +63,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php index fc08f2b73e..c4bf167f30 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -91,8 +91,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } if ($requiredHost) { @@ -269,8 +274,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php index 291be14757..16ea1ad4e5 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -46,8 +46,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $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]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php index 6bec91e309..5e370d8345 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -66,8 +66,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php index dee606f0a9..6c01afb098 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -83,8 +83,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } $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]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php index 839481ba2d..746082cbbe 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher6.php @@ -44,8 +44,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + break; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + break; + } } $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]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php index 07c8af6669..68547bf391 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -79,8 +79,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$trimmedPathinfo]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + if (!$hasTrailingSlash && '/' === $pathinfo[-1]) { + return null; + } + if ($hasTrailingSlash && '/' !== $pathinfo[-1]) { + return null; + } } $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]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - return null; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php index ec5127e7b3..e8e71e427b 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher8.php @@ -51,8 +51,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash) = $routes[$m]; - if ('/' !== $pathinfo && $hasTrailingSlash !== ('/' === $pathinfo[-1])) { - break; + if ('/' !== $pathinfo) { + 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) { diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index c1add96271..1816ba197d 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -680,6 +680,15 @@ class UrlMatcherTest extends TestCase $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) { return new UrlMatcher($routes, $context ?: new RequestContext());