Skip to content

Commit d09f134

Browse files
authored
Ensure cached and uncached routes share same precedence when resolving actions and names (#56920)
* Ensure cached and uncached routes share same precedence * formatting * Formatting * formatting * Fix tests
1 parent 5ee0245 commit d09f134

File tree

6 files changed

+88
-5
lines changed

6 files changed

+88
-5
lines changed

src/Illuminate/Routing/RouteCollection.php

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected function addLookups($route)
7979
// If the route has a name, we will add it to the name look-up table, so that we
8080
// will quickly be able to find the route associated with a name and not have
8181
// to iterate through every route every time we need to find a named route.
82-
if ($name = $route->getName()) {
82+
if (($name = $route->getName()) && ! $this->inNameLookup($name)) {
8383
$this->nameList[$name] = $route;
8484
}
8585

@@ -88,7 +88,7 @@ protected function addLookups($route)
8888
// processing a request and easily generate URLs to the given controllers.
8989
$action = $route->getAction();
9090

91-
if (isset($action['controller'])) {
91+
if (($controller = $action['controller'] ?? null) && ! $this->inActionLookup($controller)) {
9292
$this->addToActionList($action, $route);
9393
}
9494
}
@@ -105,6 +105,28 @@ protected function addToActionList($action, $route)
105105
$this->actionList[trim($action['controller'], '\\')] = $route;
106106
}
107107

108+
/**
109+
* Determine if the given controller is in the action lookup table.
110+
*
111+
* @param string $controller
112+
* @return bool
113+
*/
114+
protected function inActionLookup($controller)
115+
{
116+
return array_key_exists($controller, $this->actionList);
117+
}
118+
119+
/**
120+
* Determine if the given name is in the name lookup table.
121+
*
122+
* @param string $name
123+
* @return bool
124+
*/
125+
protected function inNameLookup($name)
126+
{
127+
return array_key_exists($name, $this->nameList);
128+
}
129+
108130
/**
109131
* Refresh the name look-up table.
110132
*
@@ -117,8 +139,8 @@ public function refreshNameLookups()
117139
$this->nameList = [];
118140

119141
foreach ($this->allRoutes as $route) {
120-
if ($route->getName()) {
121-
$this->nameList[$route->getName()] = $route;
142+
if (($name = $route->getName()) && ! $this->inNameLookup($name)) {
143+
$this->nameList[$name] = $route;
122144
}
123145
}
124146
}
@@ -135,7 +157,7 @@ public function refreshActionLookups()
135157
$this->actionList = [];
136158

137159
foreach ($this->allRoutes as $route) {
138-
if (isset($route->getAction()['controller'])) {
160+
if (($controller = $route->getAction()['controller'] ?? null) && ! $this->inActionLookup($controller)) {
139161
$this->addToActionList($route->getAction(), $route);
140162
}
141163
}

tests/Integration/Routing/CompiledRouteCollectionTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,38 @@ public function testRouteCollectionCanRetrieveByAction()
9494
$this->assertSame($action, Arr::except($route->getAction(), 'as'));
9595
}
9696

97+
public function testCompiledAndNonCompiledUrlResolutionHasSamePrecedenceForActions()
98+
{
99+
@unlink(__DIR__.'/Fixtures/cache/routes-v7.php');
100+
$this->app->useBootstrapPath(__DIR__.'/Fixtures');
101+
$app = (static function () {
102+
$refresh = true;
103+
104+
return require __DIR__.'/Fixtures/app.php';
105+
})();
106+
$app['router']->get('/foo/{bar}', ['FooController', 'show']);
107+
$app['router']->get('/foo/{bar}/{baz}', ['FooController', 'show']);
108+
$app['router']->getRoutes()->refreshActionLookups();
109+
110+
$this->assertSame('foo/{bar}', $app['router']->getRoutes()->getByAction('FooController@show')->uri);
111+
112+
$this->artisan('route:cache')->assertExitCode(0);
113+
require __DIR__.'/Fixtures/cache/routes-v7.php';
114+
115+
$this->assertSame('foo/{bar}', $app['router']->getRoutes()->getByAction('FooController@show')->uri);
116+
117+
unlink(__DIR__.'/Fixtures/cache/routes-v7.php');
118+
}
119+
120+
public function testCompiledAndNonCompiledUrlResolutionHasSamePrecedenceForNames()
121+
{
122+
$this->router->get('/foo/{bar}', ['FooController', 'show'])->name('foo.show');
123+
$this->router->get('/foo/{bar}/{baz}', ['FooController', 'show'])->name('foo.show');
124+
$this->router->getRoutes()->refreshNameLookups();
125+
126+
$this->assertSame('foo/{bar}', $this->router->getRoutes()->getByName('foo.show')->uri);
127+
}
128+
97129
public function testRouteCollectionCanGetIterator()
98130
{
99131
$this->routeCollection->add($this->newRoute('GET', 'foo/index', [
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Routing\Fixtures;
4+
5+
use Illuminate\Foundation\Application;
6+
7+
if (! class_exists(AppCache::class)) {
8+
class AppCache
9+
{
10+
public static $app;
11+
}
12+
}
13+
14+
if (isset($refresh)) {
15+
return AppCache::$app = Application::configure(basePath: __DIR__)->create();
16+
} else {
17+
return AppCache::$app ??= Application::configure(basePath: __DIR__)->create();
18+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

tests/Routing/RouteRegistrarTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,8 @@ public function testCanSetShallowOptionOnRegisteredResource()
719719
public function testCanSetScopedOptionOnRegisteredResource()
720720
{
721721
$this->router->resource('users.tasks', RouteRegistrarControllerStub::class)->scoped();
722+
$this->router->getRoutes()->refreshNameLookups();
723+
722724
$this->assertSame(
723725
['user' => null],
724726
$this->router->getRoutes()->getByName('users.tasks.index')->bindingFields()
@@ -731,6 +733,7 @@ public function testCanSetScopedOptionOnRegisteredResource()
731733
$this->router->resource('users.tasks', RouteRegistrarControllerStub::class)->scoped([
732734
'task' => 'slug',
733735
]);
736+
$this->router->getRoutes()->refreshNameLookups();
734737
$this->assertSame(
735738
['user' => null],
736739
$this->router->getRoutes()->getByName('users.tasks.index')->bindingFields()
@@ -904,6 +907,7 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource()
904907
->middlewareFor('index', RouteRegistrarMiddlewareStub::class)
905908
->middlewareFor(['create', 'store'], 'one')
906909
->middlewareFor(['edit'], ['one', 'two']);
910+
$this->router->getRoutes()->refreshNameLookups();
907911

908912
$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), ['default', RouteRegistrarMiddlewareStub::class]);
909913
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
@@ -918,6 +922,7 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredResource()
918922
->middlewareFor(['create', 'store'], 'one')
919923
->middlewareFor(['edit'], ['one', 'two'])
920924
->middleware('default');
925+
$this->router->getRoutes()->refreshNameLookups();
921926

922927
$this->assertEquals($this->router->getRoutes()->getByName('users.index')->gatherMiddleware(), [RouteRegistrarMiddlewareStub::class, 'default']);
923928
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
@@ -1448,6 +1453,7 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonReso
14481453
->middlewareFor('show', RouteRegistrarMiddlewareStub::class)
14491454
->middlewareFor(['create', 'store'], 'one')
14501455
->middlewareFor(['edit'], ['one', 'two']);
1456+
$this->router->getRoutes()->refreshNameLookups();
14511457

14521458
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['default', 'one']);
14531459
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['default', 'one']);
@@ -1463,6 +1469,7 @@ public function testCanSetMiddlewareForSpecifiedMethodsOnRegisteredSingletonReso
14631469
->middlewareFor(['create', 'store'], 'one')
14641470
->middlewareFor(['edit'], ['one', 'two'])
14651471
->middleware('default');
1472+
$this->router->getRoutes()->refreshNameLookups();
14661473

14671474
$this->assertEquals($this->router->getRoutes()->getByName('users.create')->gatherMiddleware(), ['one', 'default']);
14681475
$this->assertEquals($this->router->getRoutes()->getByName('users.store')->gatherMiddleware(), ['one', 'default']);

0 commit comments

Comments
 (0)