Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 513cc5c

Browse files
committed
[Routing] Support for Route attributes.
1 parent 5b6139f commit 513cc5c

File tree

5 files changed

+123
-11
lines changed

5 files changed

+123
-11
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Routing\Attribute;
13+
14+
use PhpAttribute;
15+
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
16+
17+
<<PhpAttribute>>
18+
class Route extends RouteAnnotation
19+
{
20+
public function __construct(string $path, array $options = [])
21+
{
22+
parent::__construct(array_merge(
23+
$options,
24+
['path' => $path]
25+
));
26+
}
27+
}

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

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ abstract class AnnotationClassLoader implements LoaderInterface
6363
*/
6464
protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route';
6565

66+
/**
67+
* @var string
68+
*/
69+
protected $routeAttributeClass = 'Symfony\\Component\\Routing\\Attribute\\Route';
70+
6671
/**
6772
* @var int
6873
*/
6974
protected $defaultRouteIndex = 0;
7075

71-
public function __construct(Reader $reader)
76+
public function __construct(Reader $reader = null)
7277
{
7378
$this->reader = $reader;
7479
}
@@ -108,19 +113,15 @@ public function load($class, string $type = null)
108113

109114
foreach ($class->getMethods() as $method) {
110115
$this->defaultRouteIndex = 0;
111-
foreach ($this->reader->getMethodAnnotations($method) as $annot) {
112-
if ($annot instanceof $this->routeAnnotationClass) {
113-
$this->addRoute($collection, $annot, $globals, $class, $method);
114-
}
116+
foreach ($this->getAnnotations($method) as $annot) {
117+
$this->addRoute($collection, $annot, $globals, $class, $method);
115118
}
116119
}
117120

118121
if (0 === $collection->count() && $class->hasMethod('__invoke')) {
119122
$globals = $this->resetGlobals();
120-
foreach ($this->reader->getClassAnnotations($class) as $annot) {
121-
if ($annot instanceof $this->routeAnnotationClass) {
122-
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
123-
}
123+
foreach ($this->getAnnotations($class) as $annot) {
124+
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
124125
}
125126
}
126127

@@ -130,7 +131,7 @@ public function load($class, string $type = null)
130131
/**
131132
* @param RouteAnnotation $annot or an object that exposes a similar interface
132133
*/
133-
protected function addRoute(RouteCollection $collection, $annot, array $globals, \ReflectionClass $class, \ReflectionMethod $method)
134+
protected function addRoute(RouteCollection $collection, object $annot, array $globals, \ReflectionClass $class, \ReflectionMethod $method)
134135
{
135136
$name = $annot->getName();
136137
if (null === $name) {
@@ -257,7 +258,15 @@ protected function getGlobals(\ReflectionClass $class)
257258
{
258259
$globals = $this->resetGlobals();
259260

260-
if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
261+
$annot = null;
262+
if (method_exists($class, 'getAttributes') && ($attribute = $class->getAttributes($this->routeAttributeClass)[0] ?? null)) {
263+
$annot = $attribute->newInstance();
264+
}
265+
if (!$annot && $this->reader) {
266+
$annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass);
267+
}
268+
269+
if ($annot) {
261270
if (null !== $annot->getName()) {
262271
$globals['name'] = $annot->getName();
263272
}
@@ -331,4 +340,32 @@ protected function createRoute(string $path, array $defaults, array $requirement
331340
}
332341

333342
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot);
343+
344+
/**
345+
* @param \ReflectionClass|\ReflectionMethod $reflection
346+
*
347+
* @return iterable<RouteAnnotation>
348+
*/
349+
private function getAnnotations(object $reflection): iterable
350+
{
351+
if (method_exists($reflection, 'getAttributes')) {
352+
foreach ($reflection->getAttributes($this->routeAttributeClass) as $attribute) {
353+
yield $attribute->newInstance();
354+
}
355+
}
356+
357+
if (!$this->reader) {
358+
return;
359+
}
360+
361+
$anntotations = $reflection instanceof \ReflectionClass
362+
? $this->reader->getClassAnnotations($reflection)
363+
: $this->reader->getMethodAnnotations($reflection);
364+
365+
foreach ($anntotations as $annotation) {
366+
if ($annotation instanceof $this->routeAnnotationClass) {
367+
yield $annotation;
368+
}
369+
}
370+
}
334371
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
class ActionPathController
8+
{
9+
<<Route('/path', ['name' => 'action'])>>
10+
public function action()
11+
{
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
<<Route('/here', ['name' => 'lol', 'methods' => ["GET", "POST"], 'schemes' => ['https']])>>
8+
class InvokableController
9+
{
10+
public function __invoke()
11+
{
12+
}
13+
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ public function testSimplePathRoute()
9090
$this->assertEquals('/path', $routes->get('action')->getPath());
9191
}
9292

93+
/**
94+
* @requires PHP 8
95+
*/
96+
public function testSimplePathRouteWithAttribute()
97+
{
98+
$routes = $this->loader->load(\Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\ActionPathController::class);
99+
$this->assertCount(1, $routes);
100+
$this->assertEquals('/path', $routes->get('action')->getPath());
101+
}
102+
93103
public function testRequirementsWithoutPlaceholderName()
94104
{
95105
$this->expectException(\InvalidArgumentException::class);
@@ -107,6 +117,18 @@ public function testInvokableControllerLoader()
107117
$this->assertEquals(['https'], $routes->get('lol')->getSchemes());
108118
}
109119

120+
/**
121+
* @requires PHP 8
122+
*/
123+
public function testInvokableControllerWithAttributes()
124+
{
125+
$routes = $this->loader->load(\Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\InvokableController::class);
126+
$this->assertCount(1, $routes);
127+
$this->assertEquals('/here', $routes->get('lol')->getPath());
128+
$this->assertEquals(['GET', 'POST'], $routes->get('lol')->getMethods());
129+
$this->assertEquals(['https'], $routes->get('lol')->getSchemes());
130+
}
131+
110132
public function testInvokableLocalizedControllerLoading()
111133
{
112134
$routes = $this->loader->load(InvokableLocalizedController::class);

0 commit comments

Comments
 (0)