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

Skip to content

Commit 93e199c

Browse files
committed
[Routing] Support for Route attributes.
1 parent 812a4d5 commit 93e199c

30 files changed

+543
-141
lines changed

.github/patch-types.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php'):
3232
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php'):
3333
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php80Dummy.php'):
34+
case false !== strpos($file, '/src/Symfony/Component/Routing/Attribute/Route.php'):
3435
case false !== strpos($file, '/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectOuter.php'):
3536
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'):
3637
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php') && \PHP_VERSION_ID < 70400:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Route
3838
*
3939
* @throws \BadMethodCallException
4040
*/
41-
public function __construct(array $data)
41+
public function __construct(array $data = [])
4242
{
4343
if (isset($data['localized_paths'])) {
4444
throw new \BadMethodCallException(sprintf('Unknown property "localized_paths" on annotation "%s".', static::class));
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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 Attribute;
15+
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
16+
17+
<<Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)>>
18+
class Route extends RouteAnnotation
19+
{
20+
}

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

Lines changed: 49 additions & 12 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
}
@@ -330,5 +339,33 @@ protected function createRoute(string $path, array $defaults, array $requirement
330339
return new Route($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
331340
}
332341

333-
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot);
342+
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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\Tests\Fixtures\AnnotationFixtures;
13+
14+
use Symfony\Component\Routing\Annotation\Route;
15+
16+
/**
17+
* @Route("/1", name="route1", schemes={"https"}, methods={"GET"})
18+
* @Route("/2", name="route2", schemes={"https"}, methods={"GET"})
19+
*/
20+
class BazClass
21+
{
22+
public function __invoke()
23+
{
24+
}
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
4+
5+
use Symfony\Component\Routing\Annotation\Route;
6+
7+
class EncodingClass
8+
{
9+
/**
10+
* @Route
11+
*/
12+
public function routeÀction()
13+
{
14+
}
15+
}
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' => '/path', 'name' => 'action'])>>
10+
public function action()
11+
{
12+
}
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\Tests\Fixtures\AttributeFixtures;
13+
14+
use Symfony\Component\Routing\Attribute\Route;
15+
16+
<<Route(['path' => '/1', 'name' => 'route1', 'schemes' => ['https'], 'methods' => ['GET']])>>
17+
<<Route(['path' => '/2', 'name' => 'route2', 'schemes' => ['https'], 'methods' => ['GET']])>>
18+
class BazClass
19+
{
20+
public function __invoke()
21+
{
22+
}
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
class DefaultValueController
8+
{
9+
<<Route(['path' => '/{default}/path', 'name' => 'action'])>>
10+
public function action($default = 'value')
11+
{
12+
}
13+
14+
<<Route(['path' => '/hello/{name<\w+>}', 'name' =>'hello_without_default'])>>
15+
<<Route(['path' => 'hello/{name<\w+>?Symfony}', 'name' =>'hello_with_default'])>>
16+
public function hello(string $name = 'World')
17+
{
18+
}
19+
}
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 EncodingClass
8+
{
9+
<<Route>>
10+
public function routeÀction()
11+
{
12+
}
13+
}
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 ExplicitLocalizedActionPathController
8+
{
9+
<<Route(['path' => ['en' => '/path', 'nl' => '/pad'], 'name' => 'action'])>>
10+
public function action()
11+
{
12+
}
13+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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\Tests\Fixtures\AttributeFixtures;
13+
14+
use Symfony\Component\Routing\Attribute\Route;
15+
16+
<<Route(['path' => '/defaults', 'locale' => 'g_locale', 'format' => 'g_format'])>>
17+
class GlobalDefaultsClass
18+
{
19+
<<Route(['path' => '/specific-locale', 'name' => 'specific_locale', 'locale' => 's_locale'])>>
20+
public function locale()
21+
{
22+
}
23+
24+
<<Route(['path' => '/specific-format', 'name' => 'specific_format', 'format' => 's_format'])>>
25+
public function format()
26+
{
27+
}
28+
}
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(['path' => '/here', 'name' => 'lol', 'methods' => ["GET", "POST"], 'schemes' => ['https']])>>
8+
class InvokableController
9+
{
10+
public function __invoke()
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(['path' => ["nl" => "/hier", "en" => "/here"], 'name' => 'action'])>>
8+
class InvokableLocalizedController
9+
{
10+
public function __invoke()
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+
class LocalizedActionPathController
8+
{
9+
<<Route(['path' => ['en' => '/path', 'nl' => '/pad'], 'name' => 'action'])>>
10+
public function action()
11+
{
12+
}
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
<<Route(['path' => ['en' => '/the/path', 'nl' => '/het/pad']])>>
8+
class LocalizedMethodActionControllers
9+
{
10+
<<Route(['name' => 'post', 'methods' => ['POST']])>>
11+
public function post()
12+
{
13+
}
14+
15+
<<Route(['name' => 'put', 'methods' => ['PUT']])>>
16+
public function put()
17+
{
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
<<Route(['path' => ['nl' => '/nl', 'en' => '/en']])>>
8+
class LocalizedPrefixLocalizedActionController
9+
{
10+
<<Route(['path' => ['nl' => '/actie', 'en' => '/action'], 'name' => 'action'])>>
11+
public function action()
12+
{
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
4+
5+
use Symfony\Component\Routing\Attribute\Route;
6+
7+
<<Route(['path' => ['en' => '/en', 'nl' => '/nl']])>>
8+
class LocalizedPrefixWithRouteWithoutLocale
9+
{
10+
<<Route(['path' => '/suffix', 'name' => 'action'])>>
11+
public function action()
12+
{
13+
}
14+
}

0 commit comments

Comments
 (0)