diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index d1b9e3ed2f..0dc0a178a8 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -564,7 +564,7 @@ EOF; } }\n" : '', $this->supportsRedirections ? " - if (!\$requiredMethods || isset(\$requiredMethods['GET'])) { + if ((!\$requiredMethods || isset(\$requiredMethods['GET'])) && 'GET' === \$canonicalMethod) { return \$allow = \$allowSchemes = array(); }" : '' ); @@ -628,34 +628,44 @@ EOF; $matches = (bool) $compiledRoute->getPathVariables(); $hostMatches = (bool) $compiledRoute->getHostVariables(); $methods = array_flip($route->getMethods()); + $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); $code = " // $name"; if ('/' === $route->getPath()) { $code .= "\n"; } elseif (!$matches) { $code .= sprintf(" - if ('/' !== \$pathinfo && '/' %s \$pathinfo[-1]) { - %s; + if ('/' !== \$pathinfo && '/' %s \$pathinfo[-1]) {%s + goto $gotoname; }\n\n", $hasTrailingSlash ? '!==' : '===', - $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return $allow = $allowSchemes = array()' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? " + if ('GET' === \$canonicalMethod) { + return \$allow = \$allowSchemes = array(); + }" : '' ); } elseif ($hasTrailingSlash) { $code .= sprintf(" - if ('/' !== \$pathinfo[-1]) { - %s; + if ('/' !== \$pathinfo[-1]) {%s + goto $gotoname; } 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 $allow = $allowSchemes = array()' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? " + if ('GET' === \$canonicalMethod) { + return \$allow = \$allowSchemes = array(); + }" : '' ); } else { $code .= sprintf(" - if ('/' !== \$pathinfo && '/' === \$pathinfo[-1] && preg_match(\$regex, substr(\$pathinfo, 0, -1), \$n) && \$m === (int) \$n['MARK']) { - %s; + if ('/' !== \$pathinfo && '/' === \$pathinfo[-1] && preg_match(\$regex, substr(\$pathinfo, 0, -1), \$n) && \$m === (int) \$n['MARK']) {%s + goto $gotoname; }\n\n", - $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? 'return $allow = $allowSchemes = array()' : 'break' + $this->supportsRedirections && (!$methods || isset($methods['GET'])) ? " + if ('GET' === \$canonicalMethod) { + return \$allow = \$allowSchemes = array(); + }" : '' ); } @@ -687,8 +697,6 @@ EOF; $code = $this->indent($code); } - $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); - // the offset where the return value is appended below, with indendation $retOffset = 12 + \strlen($code); $defaults = $route->getDefaults(); @@ -770,16 +778,10 @@ EOF; $code = substr_replace($code, 'return', $retOffset, 6); } if ($conditions) { - $code .= " }\n"; - } elseif ($schemes || $methods) { - $code .= ' '; + $code = $this->indent($code)." }\n"; } - if ($schemes || $methods) { - $code .= " $gotoname:\n"; - } - - return $conditions ? $this->indent($code) : $code; + return $code." $gotoname:\n"; } private function getExpressionLanguage() diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index 2e7148d390..f8379b41c5 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -130,6 +130,10 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface */ protected function matchCollection($pathinfo, RouteCollection $routes) { + // HEAD and GET are equivalent as per RFC + if ('HEAD' === $method = $this->context->getMethod()) { + $method = 'GET'; + } $supportsTrailingSlash = '/' !== $pathinfo && '' !== $pathinfo && $this instanceof RedirectableUrlMatcherInterface; foreach ($routes as $name => $route) { @@ -140,7 +144,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface // check the static prefix of the URL first. Only use the more expensive preg_match when it matches if ('' === $staticPrefix || 0 === strpos($pathinfo, $staticPrefix)) { // no-op - } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods))) { + } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods)) || 'GET' !== $method) { continue; } elseif ('/' === $staticPrefix[-1] && substr($staticPrefix, 0, -1) === $pathinfo) { return $this->allow = $this->allowSchemes = array(); @@ -170,7 +174,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface } } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || \in_array('GET', $requiredMethods)) { + if ((!$requiredMethods || \in_array('GET', $requiredMethods)) && 'GET' === $method) { return $this->allow = $this->allowSchemes = array(); } continue; @@ -190,11 +194,6 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface $hasRequiredScheme = !$route->getSchemes() || $route->hasScheme($this->context->getScheme()); if ($requiredMethods) { - // HEAD and GET are equivalent as per RFC - if ('HEAD' === $method = $this->context->getMethod()) { - $method = 'GET'; - } - if (!\in_array($method, $requiredMethods)) { if ($hasRequiredScheme) { $this->allow = array_merge($this->allow, $requiredMethods); 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 340a38ffe8..c5074514e8 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -143,17 +143,18 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // baz4 if ('/' !== $pathinfo[-1]) { - break; + goto not_baz4; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; } return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array()); + not_baz4: // baz5 if ('/' !== $pathinfo[-1]) { - break; + goto not_baz5; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; @@ -170,7 +171,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // baz.baz6 if ('/' !== $pathinfo[-1]) { - break; + goto not_bazbaz6; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; @@ -191,7 +192,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // foo1 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_foo1; } $ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array()); @@ -209,10 +210,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // foo2 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_foo2; } return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); + not_foo2: break; case 279: @@ -220,10 +222,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // foo3 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_foo3; } return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array()); + not_foo3: break; default: 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 3de6807188..5621ec968c 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php @@ -128,7 +128,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher13.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher13.php index 55ceb3f675..23c9327857 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher13.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher13.php @@ -46,17 +46,19 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher // r1 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_r1; } return $this->mergeDefaults(array('_route' => 'r1') + $matches, array()); + not_r1: // r2 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_r2; } return $this->mergeDefaults(array('_route' => 'r2') + $matches, array()); + not_r2: break; } 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 8e93a9bfdc..d8d0e3bc37 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -93,7 +93,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; @@ -183,17 +183,21 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // baz4 if ('/' !== $pathinfo[-1]) { - return $allow = $allowSchemes = array(); + if ('GET' === $canonicalMethod) { + return $allow = $allowSchemes = array(); + } + goto not_baz4; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; } return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array()); + not_baz4: // baz5 if ('/' !== $pathinfo[-1]) { - break; + goto not_baz5; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; @@ -210,7 +214,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // baz.baz6 if ('/' !== $pathinfo[-1]) { - break; + goto not_bazbaz6; } if ('/' !== $pathinfo && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { $matches = $n; @@ -231,7 +235,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // foo1 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - break; + goto not_foo1; } $ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array()); @@ -249,10 +253,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // foo2 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - return $allow = $allowSchemes = array(); + if ('GET' === $canonicalMethod) { + return $allow = $allowSchemes = array(); + } + goto not_foo2; } return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); + not_foo2: break; case 279: @@ -260,10 +268,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec // foo3 if ('/' !== $pathinfo && '/' === $pathinfo[-1] && preg_match($regex, substr($pathinfo, 0, -1), $n) && $m === (int) $n['MARK']) { - return $allow = $allowSchemes = array(); + if ('GET' === $canonicalMethod) { + return $allow = $allowSchemes = array(); + } + goto not_foo3; } return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array()); + not_foo3: break; default: @@ -299,7 +311,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; 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 c3f1a8da33..a6d11f6b7e 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -30,12 +30,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher case '/with-condition': // with-condition if ('/' !== $pathinfo && '/' === $pathinfo[-1]) { - break; + goto not_withcondition; } if (($context->getMethod() == "GET")) { return array('_route' => 'with-condition'); } + not_withcondition: break; default: $routes = array( 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 83a344c214..97ace0d510 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -30,7 +30,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher case '/put_and_post': // put_and_post if ('/' !== $pathinfo && '/' === $pathinfo[-1]) { - break; + goto not_put_and_post; } $ret = array('_route' => 'put_and_post'); @@ -43,7 +43,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher not_put_and_post: // put_and_get_and_head if ('/' !== $pathinfo && '/' === $pathinfo[-1]) { - break; + goto not_put_and_get_and_head; } $ret = array('_route' => 'put_and_get_and_head'); 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 f624997c8a..c8d7b40e95 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher5.php @@ -85,7 +85,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; @@ -136,7 +136,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; 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 1940455f51..b3513bc3f7 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher7.php @@ -81,7 +81,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' !== $pathinfo) { if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; @@ -148,7 +148,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec } if ($hasTrailingSlash !== ('/' === $pathinfo[-1])) { - if (!$requiredMethods || isset($requiredMethods['GET'])) { + if ((!$requiredMethods || isset($requiredMethods['GET'])) && 'GET' === $canonicalMethod) { return $allow = $allowSchemes = array(); } break; diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php index dbea6ab94f..8b567addeb 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher9.php @@ -33,14 +33,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { return $this->mergeDefaults(array('_route' => 'a') + $hostMatches, array()); } + not_a: // c if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { return $this->mergeDefaults(array('_route' => 'c') + $hostMatches, array()); } + not_c: // b if ('d.c.b.a' === $host) { return array('_route' => 'b'); } + not_b: break; } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index 746b26dc81..159f272586 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -720,6 +720,17 @@ class UrlMatcherTest extends TestCase 'customerId' => '123', ); $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); + + $coll = new RouteCollection(); + $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', array(), array(), array(), '', array(), array('get'))); + $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', array(), array(), array(), '', array(), array('post'))); + + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'POST')); + $expected = array( + '_route' => 'b', + 'customerId' => '123', + ); + $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)