diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index 28ab3679f9..ac48ca978f 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -217,7 +217,7 @@ EOF; $methods[] = 'HEAD'; } - $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods)); + $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('GET', $methods)); if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#', $compiledRoute->getRegex(), $m)) { if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) { @@ -258,6 +258,42 @@ EOF; EOF; $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); + + if ($hasTrailingSlash) { + $code .= <<context->getMethod(), array('HEAD', 'GET'))) { + \$allow[] = 'GET'; + goto $gotoname; + } else { + return \$this->redirect(\$rawPathinfo.'/', '$name'); + } + + +EOF; + } + + if ($schemes = $route->getSchemes()) { + if (!$supportsRedirections) { + throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); + } + $schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); + $code .= <<context->getScheme()])) { + if (!in_array(\$this->context->getMethod(), array('HEAD', 'GET'))) { + \$allow[] = 'GET'; + goto $gotoname; + } + + return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)); + } + + +EOF; + } + if ($methods) { if (1 === count($methods)) { $code .= <<context->getMethod(), array('HEAD', 'GET'))) { - goto $gotoname; - } else { - return \$this->redirect(\$rawPathinfo.'/', '$name'); - } - - -EOF; - } - - if ($schemes = $route->getSchemes()) { - if (!$supportsRedirections) { - throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); - } - $schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); - $code .= <<context->getScheme()])) { - return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)); - } - - -EOF; - } - // optimize parameters array if ($matches || $hostMatches) { $vars = array(); @@ -333,7 +340,7 @@ EOF; } $code .= " }\n"; - if ($methods || $hasTrailingSlash) { + if ($hasTrailingSlash || $schemes || $methods) { $code .= " $gotoname:\n"; } 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 a7e7ce75be..467d53b3ed 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -69,6 +69,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' === substr($pathinfo, -1)) { // no-op } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + $allow[] = 'GET'; goto not_baz3; } else { return $this->redirect($rawPathinfo.'/', 'baz3'); @@ -85,6 +86,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' === substr($pathinfo, -1)) { // no-op } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + $allow[] = 'GET'; goto not_baz4; } else { return $this->redirect($rawPathinfo.'/', 'baz4'); @@ -183,6 +185,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/' === substr($pathinfo, -1)) { // no-op } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + $allow[] = 'GET'; goto not_hey; } else { return $this->redirect($rawPathinfo.'/', 'hey'); @@ -333,21 +336,33 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec if ('/secure' === $pathinfo) { $requiredSchemes = array ( 'https' => 0,); if (!isset($requiredSchemes[$this->context->getScheme()])) { + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + $allow[] = 'GET'; + goto not_secure; + } + return $this->redirect($rawPathinfo, 'secure', key($requiredSchemes)); } return array('_route' => 'secure'); } + not_secure: // nonsecure if ('/nonsecure' === $pathinfo) { $requiredSchemes = array ( 'http' => 0,); if (!isset($requiredSchemes[$this->context->getScheme()])) { + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + $allow[] = 'GET'; + goto not_nonsecure; + } + return $this->redirect($rawPathinfo, 'nonsecure', key($requiredSchemes)); } return array('_route' => 'nonsecure'); } + not_nonsecure: throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } diff --git a/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php index 28f65aeeb5..7ea8aa2834 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php @@ -19,6 +19,14 @@ use Symfony\Component\Routing\RequestContext; class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest { + /** + * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException + */ + public function testRedirectWhenNoSlashForNonSafeMethod() + { + parent::testRedirectWhenNoSlashForNonSafeMethod(); + } + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { static $i = 0;