浏览代码

[Routing] decoupled Routing from FrameworkBundle

Fabien Potencier 14 年之前
父节点
当前提交
e3679ef44f

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

@@ -39,15 +39,19 @@ class PhpMatcherDumper extends MatcherDumper
             'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
         ), $options);
 
+        // trailing slash support is only enabled if we know how to redirect the user
+        $interfaces = class_implements($options['base_class']);
+        $supportsTrailingSlash = isset($interfaces['Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface']);
+
         return
             $this->startClass($options['class'], $options['base_class']).
             $this->addConstructor().
-            $this->addMatcher().
+            $this->addMatcher($supportsTrailingSlash).
             $this->endClass()
         ;
     }
 
-    private function addMatcher()
+    private function addMatcher($supportsTrailingSlash)
     {
         $code = array();
 
@@ -57,7 +61,7 @@ class PhpMatcherDumper extends MatcherDumper
             $hasTrailingSlash = false;
             $matches = false;
             if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
-                if (substr($m['url'], -1) === '/') {
+                if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
                     $conditions[] = sprintf("rtrim(\$pathinfo, '/') === '%s'", rtrim(str_replace('\\', '', $m['url']), '/'));
                     $hasTrailingSlash = true;
                 } else {
@@ -69,7 +73,7 @@ class PhpMatcherDumper extends MatcherDumper
                 }
 
                 $regex = $compiledRoute->getRegex();
-                if ($pos = strpos($regex, '/$')) {
+                if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
                     $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
                     $hasTrailingSlash = true;
                 }
@@ -100,7 +104,7 @@ EOF;
             if ($hasTrailingSlash) {
                 $code[] = sprintf(<<<EOF
             if (substr(\$pathinfo, -1) !== '/') {
-                return array('_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction', 'url' => \$this->context['base_url'].\$pathinfo.'/', 'permanent' => true, '_route' => '%s');
+                return \$this->redirect(\$pathinfo.'/', '%s');
             }
 EOF
                 , $name);

+ 37 - 0
src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php

@@ -0,0 +1,37 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Matcher;
+
+/**
+ * RedirectableUrlMatcherInterface knows how to redirect the user.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RedirectableUrlMatcherInterface
+{
+    /**
+     * Redirects the user to another URL.
+     *
+     * As the Routing component does not know know to redirect the user,
+     * the default implementation throw an exception.
+     *
+     * Override this method to implement your own logic.
+     *
+     * If you are using a Dumper, don't forget to change the default base.
+     *
+     * @param string $pathinfo The path info to redirect to.
+     * @param string $route    The route that matched
+     *
+     * @return array An array of parameters
+     */
+    function redirect($pathinfo, $route);
+}

+ 29 - 0
tests/Symfony/Tests/Component/Routing/Fixtures/RedirectableUrlMatcher.php

@@ -0,0 +1,29 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\Routing\Fixtures;
+
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
+{
+    public function redirect($pathinfo, $route)
+    {
+        return array(
+            '_controller' => 'Some controller reference...',
+            'url'         => $this->context['base_url'].$pathinfo,
+        );
+    }
+}

+ 4 - 16
tests/Symfony/Tests/Component/Routing/Fixtures/dumper/url_matcher1.php

@@ -51,45 +51,33 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
         }
 
         // baz3
-        if (rtrim($pathinfo, '/') === '/test/baz3') {
-            if (substr($pathinfo, -1) !== '/') {
-                return array('_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'url' => $this->context['base_url'].$pathinfo.'/', 'permanent' => true, '_route' => 'baz3');
-            }
+        if ($pathinfo === '/test/baz3/') {
             return array('_route' => 'baz3');
         }
 
         // baz4
-        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
-            if (substr($pathinfo, -1) !== '/') {
-                return array('_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'url' => $this->context['base_url'].$pathinfo.'/', 'permanent' => true, '_route' => 'baz4');
-            }
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
             $matches['_route'] = 'baz4';
             return $matches;
         }
 
         // baz5
-        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
             if (isset($this->context['method']) && !in_array(strtolower($this->context['method']), array('post'))) {
                 $allow = array_merge($allow, array('post'));
                 goto not_baz5;
             }
-            if (substr($pathinfo, -1) !== '/') {
-                return array('_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'url' => $this->context['base_url'].$pathinfo.'/', 'permanent' => true, '_route' => 'baz5');
-            }
             $matches['_route'] = 'baz5';
             return $matches;
         }
         not_baz5:
 
         // baz.baz6
-        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
             if (isset($this->context['method']) && !in_array(strtolower($this->context['method']), array('put'))) {
                 $allow = array_merge($allow, array('put'));
                 goto not_bazbaz6;
             }
-            if (substr($pathinfo, -1) !== '/') {
-                return array('_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', 'url' => $this->context['base_url'].$pathinfo.'/', 'permanent' => true, '_route' => 'baz.baz6');
-            }
             $matches['_route'] = 'baz.baz6';
             return $matches;
         }

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

@@ -0,0 +1,105 @@
+<?php
+
+use Symfony\Component\Routing\Matcher\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Matcher\Exception\NotFoundException;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\RedirectableUrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(array $context = array(), array $defaults = array())
+    {
+        $this->context = $context;
+        $this->defaults = $defaults;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+
+        // foo
+        if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#x', $pathinfo, $matches)) {
+            return array_merge($this->mergeDefaults($matches, array (  'def' => 'test',)), array('_route' => 'foo'));
+        }
+
+        // bar
+        if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?P<foo>[^/\.]+?)$#x', $pathinfo, $matches)) {
+            if (isset($this->context['method']) && !in_array(strtolower($this->context['method']), array('get', 'head'))) {
+                $allow = array_merge($allow, array('get', 'head'));
+                goto not_bar;
+            }
+            $matches['_route'] = 'bar';
+            return $matches;
+        }
+        not_bar:
+
+        // baz
+        if ($pathinfo === '/test/baz') {
+            return array('_route' => 'baz');
+        }
+
+        // baz2
+        if ($pathinfo === '/test/baz.html') {
+            return array('_route' => 'baz2');
+        }
+
+        // baz3
+        if (rtrim($pathinfo, '/') === '/test/baz3') {
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz3');
+            }
+            return array('_route' => 'baz3');
+        }
+
+        // baz4
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz4');
+            }
+            $matches['_route'] = 'baz4';
+            return $matches;
+        }
+
+        // baz5
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
+            if (isset($this->context['method']) && !in_array(strtolower($this->context['method']), array('post'))) {
+                $allow = array_merge($allow, array('post'));
+                goto not_baz5;
+            }
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz5');
+            }
+            $matches['_route'] = 'baz5';
+            return $matches;
+        }
+        not_baz5:
+
+        // baz.baz6
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
+            if (isset($this->context['method']) && !in_array(strtolower($this->context['method']), array('put'))) {
+                $allow = array_merge($allow, array('put'));
+                goto not_bazbaz6;
+            }
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz.baz6');
+            }
+            $matches['_route'] = 'baz.baz6';
+            return $matches;
+        }
+        not_bazbaz6:
+
+        // foofoo
+        if ($pathinfo === '/foofoo') {
+            return array (  'def' => 'test',  '_route' => 'foofoo',);
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new NotFoundException();
+    }
+}

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

@@ -76,5 +76,6 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
 
         $dumper = new PhpMatcherDumper($collection);
         $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher1.php', $dumper->dump(), '->dump() dumps basic routes to the correct PHP file.');
+        $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher2.php', $dumper->dump(array('base_class' => 'Symfony\Tests\Component\Routing\Fixtures\RedirectableUrlMatcher')), '->dump() dumps basic routes to the correct PHP file.');
     }
 }