Browse Source

[Routing] added proper support for the HEAD method

Fabien Potencier 14 năm trước cách đây
mục cha
commit
f9ffdf5b33

+ 8 - 3
src/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php

@@ -69,15 +69,20 @@ class ApacheMatcherDumper extends MatcherDumper
             $rule = array("# $name");
 
             // method mismatch
-            if ($req = strtolower($route->getRequirement('_method'))) {
+            if ($req = $route->getRequirement('_method')) {
+                $methods = explode('|', strtolower($req));
+                // GET and HEAD are equivalent
+                if (in_array('get', $methods) && !in_array('head', $methods)) {
+                    $methods[] = 'head';
+                }
                 $allow = array();
-                foreach (explode('|', $req) as $method) {
+                foreach ($methods as $method) {
                     $methodVars[] = $method;
                     $allow[] = 'E=_ROUTING__allow_'.$method.':1';
                 }
 
                 $rule[] = "RewriteCond %{REQUEST_URI} $regex";
-                $rule[] = "RewriteCond %{REQUEST_METHOD} !^($req)$ [NC]";
+                $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
                 $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
             }
 

+ 5 - 1
src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php

@@ -137,7 +137,11 @@ EOF;
 EOF;
 
         if ($req = $route->getRequirement('_method')) {
-            $methods = array_map('strtolower', explode('|', $req));
+            $methods = explode('|', strtolower($req));
+            // GET and HEAD are equivalent
+            if (in_array('get', $methods) && !in_array('head', $methods)) {
+                $methods[] = 'head';
+            }
             if (1 === count($methods)) {
                 $code[] = <<<EOF
             if (\$this->context->getMethod() != '$methods[0]') {

+ 10 - 3
src/Symfony/Component/Routing/Matcher/UrlMatcher.php

@@ -110,10 +110,17 @@ class UrlMatcher implements UrlMatcherInterface
             }
 
             // check HTTP method requirement
-            if ($route->getRequirement('_method') && ($req = explode('|', $route->getRequirement('_method'))) && !in_array($this->context->getMethod(), array_map('strtolower', $req))) {
-                $this->allow = array_merge($this->allow, $req);
+            if ($req = $route->getRequirement('_method')) {
+                // HEAD and GET are equivalent as per RFC
+                if ('head' === $method = $this->context->getMethod()) {
+                    $method = 'get';
+                }
 
-                continue;
+                if (!in_array($method, $req = explode('|', strtolower($req)))) {
+                    $this->allow = array_merge($this->allow, $req);
+
+                    continue;
+                }
             }
 
             return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));

+ 2 - 2
tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher1.apache

@@ -15,8 +15,8 @@ RewriteRule .* app.php [QSA,L,E=_ROUTING__route:bar,E=_ROUTING_foo:%1]
 
 # baragain
 RewriteCond %{REQUEST_URI} ^/baragain/([^/]+?)$
-RewriteCond %{REQUEST_METHOD} !^(get|post)$ [NC]
-RewriteRule .* - [S=1,E=_ROUTING__allow_get:1,E=_ROUTING__allow_post:1]
+RewriteCond %{REQUEST_METHOD} !^(get|post|head)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING__allow_get:1,E=_ROUTING__allow_post:1,E=_ROUTING__allow_head:1]
 RewriteCond %{REQUEST_URI} ^/baragain/([^/]+?)$
 RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baragain,E=_ROUTING_foo:%1]
 

+ 11 - 0
tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher1.php

@@ -40,6 +40,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
         }
         not_bar:
 
+        // barhead
+        if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]+?)$#x', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('get', 'head'))) {
+                $allow = array_merge($allow, array('get', 'head'));
+                goto not_barhead;
+            }
+            $matches['_route'] = 'barhead';
+            return $matches;
+        }
+        not_barhead:
+
         // baz
         if ($pathinfo === '/test/baz') {
             return array('_route' => 'baz');

+ 11 - 0
tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher2.php

@@ -40,6 +40,17 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
         }
         not_bar:
 
+        // barhead
+        if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]+?)$#x', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('get', 'head'))) {
+                $allow = array_merge($allow, array('get', 'head'));
+                goto not_barhead;
+            }
+            $matches['_route'] = 'barhead';
+            return $matches;
+        }
+        not_barhead:
+
         // baz
         if ($pathinfo === '/test/baz') {
             return array('_route' => 'baz');

+ 6 - 0
tests/Symfony/Tests/Component/Routing/Matcher/Dumper/PhpMatcherDumperTest.php

@@ -34,6 +34,12 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
             array(),
             array('_method' => 'GET|head')
         ));
+        // GET method requirement automatically adds HEAD as valid
+        $collection->add('barhead', new Route(
+            '/barhead/{foo}',
+            array(),
+            array('_method' => 'GET')
+        ));
         // simple
         $collection->add('baz', new Route(
             '/test/baz'

+ 9 - 0
tests/Symfony/Tests/Component/Routing/Matcher/UrlMatcherTest.php

@@ -44,6 +44,15 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
         }
     }
 
+    public function testHeadAllowedWhenRequirementContainsGet()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'get')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'head'));
+        $matcher->match('/foo');
+    }
+
     public function testMethodNotAllowedAggregatesAllowedMethods()
     {
         $coll = new RouteCollection();