From 7c52fa2b2dfe75d0a060e6aa90ba5c0a6ae336f9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 2 Dec 2018 12:14:59 +0100 Subject: [PATCH] [Routing] fix taking verb into account when redirecting --- .../Routing/Matcher/Dumper/PhpMatcherDumper.php | 12 ++++++------ .../Component/Routing/Matcher/UrlMatcher.php | 8 +++++--- .../Tests/Fixtures/dumper/url_matcher11.php | 6 +++--- .../Tests/Fixtures/dumper/url_matcher2.php | 14 +++++++------- .../Tests/Fixtures/dumper/url_matcher5.php | 8 ++++---- .../Tests/Fixtures/dumper/url_matcher7.php | 8 ++++---- .../Matcher/RedirectableUrlMatcherTest.php | 17 +++++++++++++++++ .../Routing/Tests/Matcher/UrlMatcherTest.php | 14 ++++++++++++++ 8 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index cb6aa6fda67bc..d1b9e3ed2f49f 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -161,10 +161,10 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array EOF - .$code."\n return null;\n }"; + .$code."\n return array();\n }"; } return " public function match(\$rawPathinfo)\n".$code."\n throw \$allow ? new MethodNotAllowedException(array_keys(\$allow)) : new ResourceNotFoundException();\n }"; @@ -565,7 +565,7 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string }\n" : '', $this->supportsRedirections ? " if (!\$requiredMethods || isset(\$requiredMethods['GET'])) { - return null; + return \$allow = \$allowSchemes = array(); }" : '' ); @@ -638,7 +638,7 @@ private function compileRoute(Route $route, string $name, bool $checkHost, bool %s; }\n\n", $hasTrailingSlash ? '!==' : '===', - $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return null' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return $allow = $allowSchemes = array()' : 'break' ); } elseif ($hasTrailingSlash) { $code .= sprintf(" @@ -648,14 +648,14 @@ private function compileRoute(Route $route, string $name, bool $checkHost, bool if ('/' !== \$pathinfo && preg_match(\$regex, substr(\$pathinfo, 0, -1), \$n) && \$m === (int) \$n['MARK']) { \$matches = \$n; }\n\n", - $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return null' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return $allow = $allowSchemes = array()' : 'break' ); } else { $code .= sprintf(" if ('/' !== \$pathinfo && '/' === \$pathinfo[-1] && preg_match(\$regex, substr(\$pathinfo, 0, -1), \$n) && \$m === (int) \$n['MARK']) { %s; }\n\n", - $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return null' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return $allow = $allowSchemes = array()' : 'break' ); } diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index f0f76c2a27f6f..2e7148d390b41 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -143,9 +143,9 @@ protected function matchCollection($pathinfo, RouteCollection $routes) } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods))) { continue; } elseif ('/' === $staticPrefix[-1] && substr($staticPrefix, 0, -1) === $pathinfo) { - return; + return $this->allow = $this->allowSchemes = array(); } elseif ('/' === $pathinfo[-1] && substr($pathinfo, 0, -1) === $staticPrefix) { - return; + return $this->allow = $this->allowSchemes = array(); } else { continue; } @@ -171,7 +171,7 @@ protected function matchCollection($pathinfo, RouteCollection $routes) } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || \in_array('GET', $requiredMethods)) { - return; + return $this->allow = $this->allowSchemes = array(); } continue; } @@ -212,6 +212,8 @@ protected function matchCollection($pathinfo, RouteCollection $routes) return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array())); } + + return array(); } /** 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 562587cbffa7a..3de6807188967 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php @@ -50,7 +50,7 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array { $allow = $allowSchemes = array(); $pathinfo = rawurldecode($rawPathinfo) ?: '/'; @@ -129,7 +129,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -167,6 +167,6 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } - return null; + return array(); } } 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 3db8bbc8b6e34..8e93a9bfdc9e5 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -50,7 +50,7 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array { $allow = $allowSchemes = array(); $pathinfo = rawurldecode($rawPathinfo) ?: '/'; @@ -94,7 +94,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -183,7 +183,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a // baz4 if ('/' !== $pathinfo[-1]) { - return null; + return $allow = $allowSchemes = array(); } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; @@ -249,7 +249,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a // foo2 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - return null; + return $allow = $allowSchemes = array(); } return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); @@ -260,7 +260,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a // foo3 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - return null; + return $allow = $allowSchemes = array(); } return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array()); @@ -300,7 +300,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -338,6 +338,6 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } - return null; + return array(); } } 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 ad94919fcb671..f624997c8ae16 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -50,7 +50,7 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array { $allow = $allowSchemes = array(); $pathinfo = rawurldecode($rawPathinfo) ?: '/'; @@ -86,7 +86,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -137,7 +137,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -175,6 +175,6 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } - return null; + return array(); } } 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 bb0bda2b2a234..1940455f51db5 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -50,7 +50,7 @@ public function match($pathinfo) throw new ResourceNotFoundException(); } - private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): array { $allow = $allowSchemes = array(); $pathinfo = rawurldecode($rawPathinfo) ?: '/'; @@ -82,7 +82,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -149,7 +149,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { if (!$requiredMethods || isset($requiredMethods['GET'])) { - return null; + return $allow = $allowSchemes = array(); } break; } @@ -187,6 +187,6 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } - return null; + return array(); } } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php index eb1703353b21e..f1d6b5325d0a5 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -170,6 +170,23 @@ public function testMissingTrailingSlashAndScheme() $matcher->match('/foo'); } + public function testSlashAndVerbPrecedenceWithRedirection() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('post'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('get'))); + + $matcher = $this->getUrlMatcher($coll); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons/')); + + $matcher->expects($this->once())->method('redirect')->with('/api/customers/123/contactpersons/')->willReturn(array()); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($routes, $context ?: new RequestContext())); diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index 136fd17c7efcc..746b26dc81566 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -708,6 +708,20 @@ public function testSlashWithVerb() $this->assertSame(array('_route' => 'b'), $matcher->match('/bar/')); } + public function testSlashAndVerbPrecedence() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('post'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('get'))); + + $matcher = $this->getUrlMatcher($coll); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext());