Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8522a83

Browse filesBrowse files
[Routing] add priority option to annotated routes
1 parent 8657314 commit 8522a83
Copy full SHA for 8522a83

File tree

9 files changed

+97
-15
lines changed
Filter options

9 files changed

+97
-15
lines changed

‎UPGRADE-5.1.md

Copy file name to clipboardExpand all lines: UPGRADE-5.1.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Routing
5757
-------
5858

5959
* Deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
60+
* Added argument `$priority` to `RouteCollection::add()`
6061

6162
Yaml
6263
----

‎UPGRADE-6.0.md

Copy file name to clipboardExpand all lines: UPGRADE-6.0.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ Routing
4747
-------
4848

4949
* Removed `RouteCollectionBuilder`.
50+
* Added argument `$priority` to `RouteCollection::add()`

‎src/Symfony/Component/Routing/Annotation/Route.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Annotation/Route.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Route
3434
private $locale;
3535
private $format;
3636
private $utf8;
37+
private $priority;
3738

3839
/**
3940
* @param array $data An array of key/value parameters
@@ -179,4 +180,14 @@ public function getCondition()
179180
{
180181
return $this->condition;
181182
}
183+
184+
public function setPriority(int $priority): void
185+
{
186+
$this->priority = $priority;
187+
}
188+
189+
public function getPriority(): ?int
190+
{
191+
return $this->priority;
192+
}
182193
}

‎src/Symfony/Component/Routing/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/CHANGELOG.md
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ CHANGELOG
44
5.1.0
55
-----
66

7-
* Deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
7+
* deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
8+
* added "priority" option to annotated routes
9+
* added argument `$priority` to `RouteCollection::add()`
810

911
5.0.0
1012
-----

‎src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
+7-6Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,8 @@ protected function addRoute(RouteCollection $collection, $annot, array $globals,
157157
$host = $globals['host'];
158158
}
159159

160-
$condition = $annot->getCondition();
161-
if (null === $condition) {
162-
$condition = $globals['condition'];
163-
}
160+
$condition = $annot->getCondition() ?? $globals['condition'];
161+
$priority = $annot->getPriority() ?? $globals['priority'];
164162

165163
$path = $annot->getLocalizedPaths() ?: $annot->getPath();
166164
$prefix = $globals['localized_paths'] ?: $globals['path'];
@@ -208,9 +206,9 @@ protected function addRoute(RouteCollection $collection, $annot, array $globals,
208206
if (0 !== $locale) {
209207
$route->setDefault('_locale', $locale);
210208
$route->setDefault('_canonical_route', $name);
211-
$collection->add($name.'.'.$locale, $route);
209+
$collection->add($name.'.'.$locale, $route, $priority);
212210
} else {
213-
$collection->add($name, $route);
211+
$collection->add($name, $route, $priority);
214212
}
215213
}
216214
}
@@ -297,6 +295,8 @@ protected function getGlobals(\ReflectionClass $class)
297295
$globals['condition'] = $annot->getCondition();
298296
}
299297

298+
$globals['priority'] = $annot->getPriority() ?? 0;
299+
300300
foreach ($globals['requirements'] as $placeholder => $requirement) {
301301
if (\is_int($placeholder)) {
302302
throw new \InvalidArgumentException(sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" in "%s"?', $placeholder, $requirement, $class->getName()));
@@ -320,6 +320,7 @@ private function resetGlobals(): array
320320
'host' => '',
321321
'condition' => '',
322322
'name' => '',
323+
'priority' => 0,
323324
];
324325
}
325326

‎src/Symfony/Component/Routing/RouteCollection.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/RouteCollection.php
+38-5Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class RouteCollection implements \IteratorAggregate, \Countable
3535
*/
3636
private $resources = [];
3737

38+
/**
39+
* @var int[]
40+
*/
41+
private $priorities = [];
42+
3843
public function __clone()
3944
{
4045
foreach ($this->routes as $name => $route) {
@@ -53,7 +58,7 @@ public function __clone()
5358
*/
5459
public function getIterator()
5560
{
56-
return new \ArrayIterator($this->routes);
61+
return new \ArrayIterator($this->all());
5762
}
5863

5964
/**
@@ -66,11 +71,22 @@ public function count()
6671
return \count($this->routes);
6772
}
6873

69-
public function add(string $name, Route $route)
74+
/**
75+
* @param int $priority
76+
*/
77+
public function add(string $name, Route $route/*, int $priority = 0*/)
7078
{
71-
unset($this->routes[$name]);
79+
if (\func_num_args() < 3 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
80+
@trigger_error(sprintf('The "%s()" method will have a new "int $priority = 0" argument in version 6.0, not defining it is deprecated since Symfony 5.1.', __METHOD__), E_USER_DEPRECATED);
81+
}
82+
83+
unset($this->routes[$name], $this->priorities[$name]);
7284

7385
$this->routes[$name] = $route;
86+
87+
if ($priority = 3 <= \func_num_args() ? func_get_arg(2) : 0) {
88+
$this->priorities[$name] = $priority;
89+
}
7490
}
7591

7692
/**
@@ -80,6 +96,14 @@ public function add(string $name, Route $route)
8096
*/
8197
public function all()
8298
{
99+
if ($this->priorities) {
100+
$priorities = $this->priorities;
101+
$keysOrder = array_flip(array_keys($this->routes));
102+
uksort($this->routes, static function ($n1, $n2) use ($priorities, $keysOrder) {
103+
return (($priorities[$n2] ?? 0) <=> ($priorities[$n1] ?? 0)) ?: ($keysOrder[$n1] <=> $keysOrder[$n2]);
104+
});
105+
}
106+
83107
return $this->routes;
84108
}
85109

@@ -101,7 +125,7 @@ public function get(string $name)
101125
public function remove($name)
102126
{
103127
foreach ((array) $name as $n) {
104-
unset($this->routes[$n]);
128+
unset($this->routes[$n], $this->priorities[$n]);
105129
}
106130
}
107131

@@ -114,8 +138,12 @@ public function addCollection(self $collection)
114138
// we need to remove all routes with the same names first because just replacing them
115139
// would not place the new route at the end of the merged array
116140
foreach ($collection->all() as $name => $route) {
117-
unset($this->routes[$name]);
141+
unset($this->routes[$name], $this->priorities[$name]);
118142
$this->routes[$name] = $route;
143+
144+
if (isset($collection->priorities[$name])) {
145+
$this->priorities[$name] = $collection->priorities[$name];
146+
}
119147
}
120148

121149
foreach ($collection->getResources() as $resource) {
@@ -147,15 +175,20 @@ public function addPrefix(string $prefix, array $defaults = [], array $requireme
147175
public function addNamePrefix(string $prefix)
148176
{
149177
$prefixedRoutes = [];
178+
$prefixedPriorities = [];
150179

151180
foreach ($this->routes as $name => $route) {
152181
$prefixedRoutes[$prefix.$name] = $route;
153182
if (null !== $name = $route->getDefault('_canonical_route')) {
154183
$route->setDefault('_canonical_route', $prefix.$name);
155184
}
185+
if (isset($this->priorities[$name])) {
186+
$prefixedPriorities[$prefix.$name] = $this->priorities[$name];
187+
}
156188
}
157189

158190
$this->routes = $prefixedRoutes;
191+
$this->priorities = $prefixedPriorities;
159192
}
160193

161194
/**

‎src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/MethodActionControllers.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/AnnotationFixtures/MethodActionControllers.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function post()
1717
}
1818

1919
/**
20-
* @Route(name="put", methods={"PUT"})
20+
* @Route(name="put", methods={"PUT"}, priority=10)
2121
*/
2222
public function put()
2323
{

‎src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public function testDefaultValuesForMethods()
144144
public function testMethodActionControllers()
145145
{
146146
$routes = $this->loader->load(MethodActionControllers::class);
147-
$this->assertCount(2, $routes);
147+
$this->assertSame(['put', 'post'], array_keys($routes->all()));
148148
$this->assertEquals('/the/path', $routes->get('put')->getPath());
149149
$this->assertEquals('/the/path', $routes->get('post')->getPath());
150150
}
@@ -178,7 +178,7 @@ public function testGlobalDefaultsRoutesLoadWithAnnotation()
178178
public function testUtf8RoutesLoadWithAnnotation()
179179
{
180180
$routes = $this->loader->load(Utf8ActionControllers::class);
181-
$this->assertCount(2, $routes);
181+
$this->assertSame(['one', 'two'], array_keys($routes->all()));
182182
$this->assertTrue($routes->get('one')->getOption('utf8'), 'The route must accept utf8');
183183
$this->assertFalse($routes->get('two')->getOption('utf8'), 'The route must not accept utf8');
184184
}

‎src/Symfony/Component/Routing/Tests/RouteCollectionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,4 +330,37 @@ public function testAddNamePrefixCanonicalRouteName()
330330
$this->assertEquals('api_bar', $collection->get('api_bar')->getDefault('_canonical_route'));
331331
$this->assertEquals('api_api_foo', $collection->get('api_api_foo')->getDefault('_canonical_route'));
332332
}
333+
334+
public function testAddWithPriority()
335+
{
336+
$collection = new RouteCollection();
337+
$collection->add('foo', $foo = new Route('/foo'), 0);
338+
$collection->add('bar', $bar = new Route('/bar'), 1);
339+
$collection->add('baz', $baz = new Route('/baz'));
340+
341+
$expected = [
342+
'bar' => $bar,
343+
'foo' => $foo,
344+
'baz' => $baz,
345+
];
346+
347+
$this->assertSame($expected, $collection->all());
348+
349+
$collection2 = new RouteCollection();
350+
$collection2->add('foo2', $foo2 = new Route('/foo'), 0);
351+
$collection2->add('bar2', $bar2 = new Route('/bar'), 1);
352+
$collection2->add('baz2', $baz2 = new Route('/baz'));
353+
$collection2->addCollection($collection);
354+
355+
$expected = [
356+
'bar2' => $bar2,
357+
'bar' => $bar,
358+
'foo2' => $foo2,
359+
'baz2' => $baz2,
360+
'foo' => $foo,
361+
'baz' => $baz,
362+
];
363+
364+
$this->assertSame($expected, $collection2->all());
365+
}
333366
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.