diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php index c04605c3f7fe..7926b625785b 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -105,8 +105,16 @@ public function match(\$pathinfo) { \$allow = array(); \$pathinfo = rawurldecode(\$pathinfo); + \$trimmedPathinfo = rtrim(\$pathinfo, '/'); \$context = \$this->context; \$request = \$this->request; + \$requestMethod = \$canonicalMethod = \$context->getMethod(); + \$scheme = \$context->getScheme(); + + if ('HEAD' === \$requestMethod) { + \$canonicalMethod = 'GET'; + } + $code @@ -133,7 +141,7 @@ private function compileRoutes(RouteCollection $routes, $supportsRedirections) foreach ($groups as $collection) { if (null !== $regex = $collection->getAttribute('host_regex')) { if (!$fetchedHost) { - $code .= " \$host = \$this->context->getHost();\n\n"; + $code .= " \$host = \$context->getHost();\n\n"; $fetchedHost = true; } @@ -217,20 +225,15 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren $hostMatches = false; $methods = $route->getMethods(); - // GET and HEAD are equivalent - if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { - $methods[] = 'HEAD'; - } - $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods)); $regex = $compiledRoute->getRegex(); if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#'.(substr($regex, -1) === 'u' ? 'u' : ''), $regex, $m)) { if ($supportsTrailingSlash && substr($m['url'], -1) === '/') { - $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); + $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); $hasTrailingSlash = true; } else { - $conditions[] = sprintf('$pathinfo === %s', var_export(str_replace('\\', '', $m['url']), true)); + $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true)); } } else { if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) { @@ -263,26 +266,57 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren EOF; $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); + if ($methods) { if (1 === count($methods)) { - $code .= <<context->getMethod() != '$methods[0]') { + if ($methods[0] === 'HEAD') { + $code .= <<context->getMethod(), array('$methods'))) { + $methodVariable = 'requestMethod'; + + if (in_array('GET', $methods)) { + // Since we treat HEAD requests like GET requests we don't need to match it. + $methodVariable = 'canonicalMethod'; + $methods = array_filter($methods, function ($method) { return 'HEAD' !== $method; }); + } + + if (1 === count($methods)) { + $code .= <<context->getScheme()])) { + if (!isset(\$requiredSchemes[\$scheme])) { return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes)); } 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 4ea0b8a1a3e7..60303595f1b4 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php @@ -24,8 +24,16 @@ public function match($pathinfo) { $allow = array(); $pathinfo = rawurldecode($pathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + // foo if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { @@ -35,8 +43,8 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/bar')) { // bar if (preg_match('#^/bar/(?P[^/]++)$#s', $pathinfo, $matches)) { - if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { - $allow = array_merge($allow, array('GET', 'HEAD')); + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; goto not_bar; } @@ -46,8 +54,8 @@ public function match($pathinfo) // barhead if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#s', $pathinfo, $matches)) { - if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { - $allow = array_merge($allow, array('GET', 'HEAD')); + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; goto not_barhead; } @@ -60,17 +68,17 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/test')) { if (0 === strpos($pathinfo, '/test/baz')) { // baz - if ($pathinfo === '/test/baz') { + if ('/test/baz' === $pathinfo) { return array('_route' => 'baz'); } // baz2 - if ($pathinfo === '/test/baz.html') { + if ('/test/baz.html' === $pathinfo) { return array('_route' => 'baz2'); } // baz3 - if ($pathinfo === '/test/baz3/') { + if ('/test/baz3/' === $pathinfo) { return array('_route' => 'baz3'); } @@ -83,7 +91,7 @@ public function match($pathinfo) // baz5 if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { - if ($this->context->getMethod() != 'POST') { + if ('POST' !== $canonicalMethod) { $allow[] = 'POST'; goto not_baz5; } @@ -94,7 +102,7 @@ public function match($pathinfo) // baz.baz6 if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { - if ($this->context->getMethod() != 'PUT') { + if ('PUT' !== $canonicalMethod) { $allow[] = 'PUT'; goto not_bazbaz6; } @@ -106,7 +114,7 @@ public function match($pathinfo) } // foofoo - if ($pathinfo === '/foofoo') { + if ('/foofoo' === $pathinfo) { return array ( 'def' => 'test', '_route' => 'foofoo',); } @@ -116,7 +124,7 @@ public function match($pathinfo) } // space - if ($pathinfo === '/spa ce') { + if ('/spa ce' === $pathinfo) { return array('_route' => 'space'); } @@ -161,12 +169,12 @@ public function match($pathinfo) } // overridden2 - if ($pathinfo === '/multi/new') { + if ('/multi/new' === $pathinfo) { return array('_route' => 'overridden2'); } // hey - if ($pathinfo === '/multi/hey/') { + if ('/multi/hey/' === $pathinfo) { return array('_route' => 'hey'); } @@ -184,7 +192,7 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/aba')) { // ababa - if ($pathinfo === '/ababa') { + if ('/ababa' === $pathinfo) { return array('_route' => 'ababa'); } @@ -195,16 +203,16 @@ public function match($pathinfo) } - $host = $this->context->getHost(); + $host = $context->getHost(); if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { // route1 - if ($pathinfo === '/route1') { + if ('/route1' === $pathinfo) { return array('_route' => 'route1'); } // route2 - if ($pathinfo === '/c2/route2') { + if ('/c2/route2' === $pathinfo) { return array('_route' => 'route2'); } @@ -212,7 +220,7 @@ public function match($pathinfo) if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) { // route3 - if ($pathinfo === '/c2/route3') { + if ('/c2/route3' === $pathinfo) { return array('_route' => 'route3'); } @@ -220,7 +228,7 @@ public function match($pathinfo) if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { // route4 - if ($pathinfo === '/route4') { + if ('/route4' === $pathinfo) { return array('_route' => 'route4'); } @@ -228,26 +236,26 @@ public function match($pathinfo) if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { // route5 - if ($pathinfo === '/route5') { + if ('/route5' === $pathinfo) { return array('_route' => 'route5'); } } // route6 - if ($pathinfo === '/route6') { + if ('/route6' === $pathinfo) { return array('_route' => 'route6'); } if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) { if (0 === strpos($pathinfo, '/route1')) { // route11 - if ($pathinfo === '/route11') { + if ('/route11' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); } // route12 - if ($pathinfo === '/route12') { + if ('/route12' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); } @@ -280,7 +288,7 @@ public function match($pathinfo) } // route17 - if ($pathinfo === '/route17') { + if ('/route17' === $pathinfo) { return array('_route' => 'route17'); } @@ -288,7 +296,7 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/a')) { // a - if ($pathinfo === '/a/a...') { + if ('/a/a...' === $pathinfo) { return array('_route' => 'a'); } 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 f9d3fa2d8257..e48d9172a1e5 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php @@ -24,8 +24,16 @@ public function match($pathinfo) { $allow = array(); $pathinfo = rawurldecode($pathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + // foo if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { @@ -35,8 +43,8 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/bar')) { // bar if (preg_match('#^/bar/(?P[^/]++)$#s', $pathinfo, $matches)) { - if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { - $allow = array_merge($allow, array('GET', 'HEAD')); + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; goto not_bar; } @@ -46,8 +54,8 @@ public function match($pathinfo) // barhead if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#s', $pathinfo, $matches)) { - if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) { - $allow = array_merge($allow, array('GET', 'HEAD')); + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; goto not_barhead; } @@ -60,17 +68,17 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/test')) { if (0 === strpos($pathinfo, '/test/baz')) { // baz - if ($pathinfo === '/test/baz') { + if ('/test/baz' === $pathinfo) { return array('_route' => 'baz'); } // baz2 - if ($pathinfo === '/test/baz.html') { + if ('/test/baz.html' === $pathinfo) { return array('_route' => 'baz2'); } // baz3 - if (rtrim($pathinfo, '/') === '/test/baz3') { + if ('/test/baz3' === $trimmedPathinfo) { if (substr($pathinfo, -1) !== '/') { return $this->redirect($pathinfo.'/', 'baz3'); } @@ -91,7 +99,7 @@ public function match($pathinfo) // baz5 if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { - if ($this->context->getMethod() != 'POST') { + if ('POST' !== $canonicalMethod) { $allow[] = 'POST'; goto not_baz5; } @@ -102,7 +110,7 @@ public function match($pathinfo) // baz.baz6 if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { - if ($this->context->getMethod() != 'PUT') { + if ('PUT' !== $canonicalMethod) { $allow[] = 'PUT'; goto not_bazbaz6; } @@ -114,7 +122,7 @@ public function match($pathinfo) } // foofoo - if ($pathinfo === '/foofoo') { + if ('/foofoo' === $pathinfo) { return array ( 'def' => 'test', '_route' => 'foofoo',); } @@ -124,7 +132,7 @@ public function match($pathinfo) } // space - if ($pathinfo === '/spa ce') { + if ('/spa ce' === $pathinfo) { return array('_route' => 'space'); } @@ -169,12 +177,12 @@ public function match($pathinfo) } // overridden2 - if ($pathinfo === '/multi/new') { + if ('/multi/new' === $pathinfo) { return array('_route' => 'overridden2'); } // hey - if (rtrim($pathinfo, '/') === '/multi/hey') { + if ('/multi/hey' === $trimmedPathinfo) { if (substr($pathinfo, -1) !== '/') { return $this->redirect($pathinfo.'/', 'hey'); } @@ -196,7 +204,7 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/aba')) { // ababa - if ($pathinfo === '/ababa') { + if ('/ababa' === $pathinfo) { return array('_route' => 'ababa'); } @@ -207,16 +215,16 @@ public function match($pathinfo) } - $host = $this->context->getHost(); + $host = $context->getHost(); if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { // route1 - if ($pathinfo === '/route1') { + if ('/route1' === $pathinfo) { return array('_route' => 'route1'); } // route2 - if ($pathinfo === '/c2/route2') { + if ('/c2/route2' === $pathinfo) { return array('_route' => 'route2'); } @@ -224,7 +232,7 @@ public function match($pathinfo) if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) { // route3 - if ($pathinfo === '/c2/route3') { + if ('/c2/route3' === $pathinfo) { return array('_route' => 'route3'); } @@ -232,7 +240,7 @@ public function match($pathinfo) if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { // route4 - if ($pathinfo === '/route4') { + if ('/route4' === $pathinfo) { return array('_route' => 'route4'); } @@ -240,26 +248,26 @@ public function match($pathinfo) if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { // route5 - if ($pathinfo === '/route5') { + if ('/route5' === $pathinfo) { return array('_route' => 'route5'); } } // route6 - if ($pathinfo === '/route6') { + if ('/route6' === $pathinfo) { return array('_route' => 'route6'); } if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) { if (0 === strpos($pathinfo, '/route1')) { // route11 - if ($pathinfo === '/route11') { + if ('/route11' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); } // route12 - if ($pathinfo === '/route12') { + if ('/route12' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); } @@ -292,7 +300,7 @@ public function match($pathinfo) } // route17 - if ($pathinfo === '/route17') { + if ('/route17' === $pathinfo) { return array('_route' => 'route17'); } @@ -300,7 +308,7 @@ public function match($pathinfo) if (0 === strpos($pathinfo, '/a')) { // a - if ($pathinfo === '/a/a...') { + if ('/a/a...' === $pathinfo) { return array('_route' => 'a'); } @@ -320,9 +328,9 @@ public function match($pathinfo) } // secure - if ($pathinfo === '/secure') { + if ('/secure' === $pathinfo) { $requiredSchemes = array ( 'https' => 0,); - if (!isset($requiredSchemes[$this->context->getScheme()])) { + if (!isset($requiredSchemes[$scheme])) { return $this->redirect($pathinfo, 'secure', key($requiredSchemes)); } @@ -330,9 +338,9 @@ public function match($pathinfo) } // nonsecure - if ($pathinfo === '/nonsecure') { + if ('/nonsecure' === $pathinfo) { $requiredSchemes = array ( 'http' => 0,); - if (!isset($requiredSchemes[$this->context->getScheme()])) { + if (!isset($requiredSchemes[$scheme])) { return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes)); } 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 d9da7b02d4b4..48cd6dfac6c1 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php @@ -24,12 +24,20 @@ public function match($pathinfo) { $allow = array(); $pathinfo = rawurldecode($pathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + if (0 === strpos($pathinfo, '/rootprefix')) { // static - if ($pathinfo === '/rootprefix/test') { + if ('/rootprefix/test' === $pathinfo) { return array('_route' => 'static'); } @@ -41,7 +49,7 @@ public function match($pathinfo) } // with-condition - if ($pathinfo === '/with-condition' && ($context->getMethod() == "GET")) { + if ('/with-condition' === $pathinfo && ($context->getMethod() == "GET")) { return array('_route' => 'with-condition'); } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php new file mode 100644 index 000000000000..70d92657d9e8 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher4.php @@ -0,0 +1,101 @@ +context = $context; + } + + public function match($pathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($pathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + // just_head + if ('/just_head' === $pathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_just_head; + } + + return array('_route' => 'just_head'); + } + not_just_head: + + // head_and_get + if ('/head_and_get' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_head_and_get; + } + + return array('_route' => 'head_and_get'); + } + not_head_and_get: + + if (0 === strpos($pathinfo, '/p')) { + // post_and_head + if ('/post_and_get' === $pathinfo) { + if (!in_array($requestMethod, array('POST', 'HEAD'))) { + $allow = array_merge($allow, array('POST', 'HEAD')); + goto not_post_and_head; + } + + return array('_route' => 'post_and_head'); + } + not_post_and_head: + + if (0 === strpos($pathinfo, '/put_and_post')) { + // put_and_post + if ('/put_and_post' === $pathinfo) { + if (!in_array($requestMethod, array('PUT', 'POST'))) { + $allow = array_merge($allow, array('PUT', 'POST')); + goto not_put_and_post; + } + + return array('_route' => 'put_and_post'); + } + not_put_and_post: + + // put_and_get_and_head + if ('/put_and_post' === $pathinfo) { + if (!in_array($canonicalMethod, array('PUT', 'GET'))) { + $allow = array_merge($allow, array('PUT', 'GET')); + goto not_put_and_get_and_head; + } + + return array('_route' => 'put_and_get_and_head'); + } + not_put_and_get_and_head: + + } + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php index cb3a1441656e..3b1d723eb919 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -279,10 +279,59 @@ public function getRouteCollections() $route->setCondition('context.getMethod() == "GET"'); $rootprefixCollection->add('with-condition', $route); + /* test case 4 */ + $headMatchCasesCollection = new RouteCollection(); + $headMatchCasesCollection->add('just_head', new Route( + '/just_head', + array(), + array(), + array(), + '', + array(), + array('HEAD') + )); + $headMatchCasesCollection->add('head_and_get', new Route( + '/head_and_get', + array(), + array(), + array(), + '', + array(), + array('GET', 'HEAD') + )); + $headMatchCasesCollection->add('post_and_head', new Route( + '/post_and_get', + array(), + array(), + array(), + '', + array(), + array('POST', 'HEAD') + )); + $headMatchCasesCollection->add('put_and_post', new Route( + '/put_and_post', + array(), + array(), + array(), + '', + array(), + array('PUT', 'POST') + )); + $headMatchCasesCollection->add('put_and_get_and_head', new Route( + '/put_and_post', + array(), + array(), + array(), + '', + array(), + array('PUT', 'GET', 'HEAD') + )); + return array( array($collection, 'url_matcher1.php', array()), array($redirectCollection, 'url_matcher2.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), array($rootprefixCollection, 'url_matcher3.php', array()), + array($headMatchCasesCollection, 'url_matcher4.php', array()), ); } }