Browse Source

Merge branch 'duplicate-route' into master.

Throw exceptions when duplicate named routes are connected.

Refs #9237
Mark Story 9 years ago
parent
commit
a7ac286998

+ 38 - 0
src/Routing/Exception/DuplicateNamedRouteException.php

@@ -0,0 +1,38 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @since         3.3.1
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Routing\Exception;
+
+use Cake\Core\Exception\Exception;
+
+/**
+ * Exception raised when a route names used twice.
+ */
+class DuplicateNamedRouteException extends Exception
+{
+
+    /**
+     * {@inheritDoc}
+     */
+    protected $_messageTemplate = 'A route named "%s" has already been connected to "%s".';
+
+    /**
+     * {@inheritDoc}
+     */
+    public function __construct($message, $code = 404)
+    {
+        if (is_array($message) && isset($message['message'])) {
+            $this->_messageTemplate = $message['message'];
+        }
+        parent::__construct($message, $code);
+    }
+}

+ 7 - 0
src/Routing/RouteCollection.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\Routing;
 
+use Cake\Routing\Exception\DuplicateNamedRouteException;
 use Cake\Routing\Exception\MissingRouteException;
 use Cake\Routing\Route\Route;
 
@@ -77,6 +78,12 @@ class RouteCollection
 
         // Explicit names
         if (isset($options['_name'])) {
+            if (isset($this->_named[$options['_name']])) {
+                throw new DuplicateNamedRouteException([
+                    'name' => $options['_name'],
+                    'url' => $this->_named[$options['_name']]->template
+                ]);
+            }
             $this->_named[$options['_name']] = $route;
         }
 

+ 69 - 0
src/Template/Error/duplicate_named_route.ctp

@@ -0,0 +1,69 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.1
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Cake\Error\Debugger;
+use Cake\Routing\Router;
+
+$this->layout = 'dev_error';
+
+$this->assign('title', 'Duplicate Named Route');
+$this->assign('templateName', 'duplicate_named_route.ctp');
+
+$attributes = $error->getAttributes();
+
+$this->start('subheading');
+?>
+    <strong>Error: </strong>
+    <?= $error->getMessage(); ?>
+<?php $this->end() ?>
+
+<?php $this->start('file') ?>
+<p>Route names must be unique across your entire application.
+The same <code>_name</code> option cannot be used twice,
+even if the names occur in different routing scopes.
+Remove duplicate route names in your route configuration.</p>
+
+<?php if (!empty($attributes['context'])) : ?>
+<p>The passed context was:</p>
+<pre>
+<?= Debugger::exportVar($attributes['context']); ?>
+</pre>
+<?php endif; ?>
+
+<h3>Connected Routes</h3>
+<table cellspacing="0" cellpadding="0">
+<tr><th>Template</th><th>Defaults</th><th>Options</th></tr>
+<?php
+$url = false;
+if (!empty($attributes['url'])) {
+    $url = $attributes['url'];
+}
+foreach (Router::routes() as $route) :
+    if (isset($route->options['_name']) && $url === $route->options['_name']) :
+        echo '<tr class="error">';
+    else :
+        echo '<tr>';
+    endif;
+    printf(
+        '<td width="25%%">%s</td><td>%s</td><td width="20%%">%s</td>',
+        $route->template,
+        Debugger::exportVar($route->defaults),
+        Debugger::exportVar($route->options)
+    );
+    echo '</tr>';
+endforeach;
+?>
+</table>
+<?php $this->end() ?>

+ 15 - 0
tests/TestCase/Routing/RouteCollectionTest.php

@@ -356,6 +356,21 @@ class RouteCollectionTest extends TestCase
     }
 
     /**
+     * Test the add() with some _name.
+     *
+     * @expectedException \Cake\Routing\Exception\DuplicateNamedRouteException
+     *
+     * @return void
+     */
+    public function testAddingDuplicateNamedRoutes()
+    {
+        $one = new Route('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
+        $two = new Route('/', ['controller' => 'Dashboards', 'action' => 'display']);
+        $this->collection->add($one, ['_name' => 'test']);
+        $this->collection->add($two, ['_name' => 'test']);
+    }
+
+    /**
      * Test basic get/set of extensions.
      *
      * @return void

+ 25 - 0
tests/TestCase/Routing/RouterTest.php

@@ -1192,6 +1192,31 @@ class RouterTest extends TestCase
     }
 
     /**
+     * Test that using duplicate names causes exceptions.
+     *
+     * @expectedException \Cake\Routing\Exception\DuplicateNamedRouteException
+     * @return void
+     */
+    public function testDuplicateNamedRouteException()
+    {
+        Router::connect(
+            '/users/:name',
+            ['controller' => 'users', 'action' => 'view'],
+            ['_name' => 'test']
+        );
+        Router::connect(
+            '/users/:name',
+            ['controller' => 'users', 'action' => 'view'],
+            ['_name' => 'otherName']
+        );
+        Router::connect(
+            '/users/:name',
+            ['controller' => 'users', 'action' => 'view'],
+            ['_name' => 'test']
+        );
+    }
+
+    /**
      * Test that url filters are applied to url params.
      *
      * @return void