diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php index 95e0d8d3a9..b4b4256385 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherTrait.php @@ -132,7 +132,7 @@ trait PhpMatcherTrait $hasTrailingVar = $trimmedPathinfo !== $pathinfo && $hasTrailingVar; - if ($hasTrailingVar && ($hasTrailingSlash || !($n = $matches[\count($vars)] ?? '') || '/' !== substr($n, -1)) && preg_match($regex, $this->matchHost ? $host.'.'.$trimmedPathinfo : $trimmedPathinfo, $n) && $m === (int) $n['MARK']) { + if ($hasTrailingVar && ($hasTrailingSlash || (null === $n = $matches[\count($vars)] ?? null) || '/' !== ($n[-1] ?? '/')) && preg_match($regex, $this->matchHost ? $host.'.'.$trimmedPathinfo : $trimmedPathinfo, $n) && $m === (int) $n['MARK']) { if ($hasTrailingSlash) { $matches = $n; } else { diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index dda9edb7e2..536ed732ad 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -158,7 +158,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface $hasTrailingVar = $trimmedPathinfo !== $pathinfo && preg_match('#\{\w+\}/?$#', $route->getPath()); - if ($hasTrailingVar && ($hasTrailingSlash || !($m = $matches[\count($compiledRoute->getPathVariables())] ?? '') || '/' !== substr($m, -1)) && preg_match($regex, $trimmedPathinfo, $m)) { + if ($hasTrailingVar && ($hasTrailingSlash || (null === $m = $matches[\count($compiledRoute->getPathVariables())] ?? null) || '/' !== ($m[-1] ?? '/')) && preg_match($regex, $trimmedPathinfo, $m)) { if ($hasTrailingSlash) { $matches = $m; } else { diff --git a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php index 5e8114ea28..f5ac21db90 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -198,15 +198,15 @@ class RedirectableUrlMatcherTest extends UrlMatcherTest $this->assertEquals(['_route' => 'a', 'a' => '123'], $matcher->match('/123/')); } - public function testTrailingRequirementWithDefault() + public function testTrailingRequirementWithDefault_A() { $coll = new RouteCollection(); - $coll->add('a', new Route('/foo/{a}', ['a' => 'bar'], ['a' => '.+'])); + $coll->add('a', new Route('/fr-fr/{a}', ['a' => 'aaa'], ['a' => '.+'])); $matcher = $this->getUrlMatcher($coll); - $matcher->expects($this->once())->method('redirect')->with('/foo')->willReturn([]); + $matcher->expects($this->once())->method('redirect')->with('/fr-fr')->willReturn([]); - $this->assertEquals(['_route' => 'a', 'a' => 'bar'], $matcher->match('/foo/')); + $this->assertEquals(['_route' => 'a', 'a' => 'aaa'], $matcher->match('/fr-fr/')); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index 596c1476f7..ac3816f893 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -757,6 +757,41 @@ class UrlMatcherTest extends TestCase $this->assertEquals(['_route' => 'a', 'a' => 'foo/'], $matcher->match('/foo/')); } + public function testTrailingRequirementWithDefault() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/fr-fr/{a}', ['a' => 'aaa'], ['a' => '.+'])); + $coll->add('b', new Route('/en-en/{b}', ['b' => 'bbb'], ['b' => '.*'])); + + $matcher = $this->getUrlMatcher($coll); + + $this->assertEquals(['_route' => 'a', 'a' => 'aaa'], $matcher->match('/fr-fr')); + $this->assertEquals(['_route' => 'a', 'a' => 'AAA'], $matcher->match('/fr-fr/AAA')); + $this->assertEquals(['_route' => 'b', 'b' => 'bbb'], $matcher->match('/en-en')); + $this->assertEquals(['_route' => 'b', 'b' => 'BBB'], $matcher->match('/en-en/BBB')); + } + + public function testTrailingRequirementWithDefault_A() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/fr-fr/{a}', ['a' => 'aaa'], ['a' => '.+'])); + + $matcher = $this->getUrlMatcher($coll); + + $this->expectException(ResourceNotFoundException::class); + $matcher->match('/fr-fr/'); + } + + public function testTrailingRequirementWithDefault_B() + { + $coll = new RouteCollection(); + $coll->add('b', new Route('/en-en/{b}', ['b' => 'bbb'], ['b' => '.*'])); + + $matcher = $this->getUrlMatcher($coll); + + $this->assertEquals(['_route' => 'b', 'b' => ''], $matcher->match('/en-en/')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext());