26
26
* time, this method should define some PHP callable to be called for the route
27
27
* (a controller in MVC speak).
28
28
*
29
- * The @ Route annotation can be set on the class (for global parameters),
29
+ * The #[ Route] attribute can be set on the class (for global parameters),
30
30
* and on each method.
31
31
*
32
- * The @ Route annotation main value is the route path. The annotation also
32
+ * The #[ Route] attribute main value is the route path. The attribute also
33
33
* recognizes several parameters: requirements, options, defaults, schemes,
34
34
* methods, host, and name. The name parameter is mandatory.
35
35
* Here is an example of how you should be able to use it:
36
- * /**
37
- * * @Route("/Blog")
38
- * * /
39
- * class Blog
40
- * {
41
- * /**
42
- * * @Route("/", name="blog_index")
43
- * * /
44
- * public function index()
45
- * {
46
- * }
47
- * /**
48
- * * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
49
- * * /
50
- * public function show()
51
- * {
52
- * }
53
- * }
54
36
*
55
- * On PHP 8, the annotation class can be used as an attribute as well:
56
37
* #[Route('/Blog')]
57
38
* class Blog
58
39
* {
71
52
*/
72
53
abstract class AnnotationClassLoader implements LoaderInterface
73
54
{
55
+ /**
56
+ * @var Reader|null
57
+ *
58
+ * @deprecated in Symfony 6.4, this property will be removed in Symfony 7.
59
+ */
74
60
protected $ reader ;
61
+
62
+ /**
63
+ * @var string|null
64
+ *
65
+ * @internal since Symfony 6.4, this property will be private in Symfony 7.
66
+ */
75
67
protected $ env ;
76
68
77
69
/**
78
70
* @var string
71
+ *
72
+ * @internal since Symfony 6.4, this property will be private in Symfony 7.
79
73
*/
80
74
protected $ routeAnnotationClass = RouteAnnotation::class;
81
75
82
76
/**
83
77
* @var int
78
+ *
79
+ * @internal since Symfony 6.4, this property will be private in Symfony 7.
84
80
*/
85
81
protected $ defaultRouteIndex = 0 ;
86
82
87
- public function __construct (Reader $ reader = null , string $ env = null )
83
+ private bool $ hasDeprecatedAnnotations = false ;
84
+
85
+ /**
86
+ * @param string|null $env
87
+ */
88
+ public function __construct ($ env = null )
88
89
{
89
- $ this ->reader = $ reader ;
90
- $ this ->env = $ env ;
90
+ if ($ env instanceof Reader || null === $ env && \func_num_args () > 1 && null !== func_get_arg (1 )) {
91
+ trigger_deprecation ('symfony/routing ' , '6.4 ' , 'Passing an instance of "%s" as first and the environment as second argument to "%s" is deprecated. Pass the environment as first argument instead. ' , Reader::class, __METHOD__ );
92
+
93
+ $ this ->reader = $ env ;
94
+ $ env = \func_num_args () > 1 ? func_get_arg (1 ) : null ;
95
+ }
96
+
97
+ if (\is_string ($ env ) || null === $ env ) {
98
+ $ this ->env = $ env ;
99
+ } elseif ($ env instanceof \Stringable || \is_scalar ($ env )) {
100
+ $ this ->env = (string ) $ env ;
101
+ } else {
102
+ throw new \TypeError (__METHOD__ .sprintf (': Parameter $env was expected to be a string or null, "%s" given. ' , get_debug_type ($ env )));
103
+ }
91
104
}
92
105
93
106
/**
@@ -116,43 +129,48 @@ public function load(mixed $class, string $type = null): RouteCollection
116
129
throw new \InvalidArgumentException (sprintf ('Annotations from class "%s" cannot be read as it is abstract. ' , $ class ->getName ()));
117
130
}
118
131
119
- $ globals = $ this ->getGlobals ($ class );
120
-
121
- $ collection = new RouteCollection ();
122
- $ collection ->addResource (new FileResource ($ class ->getFileName ()));
132
+ $ this ->hasDeprecatedAnnotations = false ;
123
133
124
- if ($ globals ['env ' ] && $ this ->env !== $ globals ['env ' ]) {
125
- return $ collection ;
126
- }
134
+ try {
135
+ $ globals = $ this ->getGlobals ($ class );
136
+ $ collection = new RouteCollection ();
137
+ $ collection ->addResource (new FileResource ($ class ->getFileName ()));
138
+ if ($ globals ['env ' ] && $ this ->env !== $ globals ['env ' ]) {
139
+ return $ collection ;
140
+ }
141
+ $ fqcnAlias = false ;
142
+ foreach ($ class ->getMethods () as $ method ) {
143
+ $ this ->defaultRouteIndex = 0 ;
144
+ $ routeNamesBefore = array_keys ($ collection ->all ());
145
+ foreach ($ this ->getAnnotations ($ method ) as $ annot ) {
146
+ $ this ->addRoute ($ collection , $ annot , $ globals , $ class , $ method );
147
+ if ('__invoke ' === $ method ->name ) {
148
+ $ fqcnAlias = true ;
149
+ }
150
+ }
127
151
128
- $ fqcnAlias = false ;
129
- foreach ($ class ->getMethods () as $ method ) {
130
- $ this ->defaultRouteIndex = 0 ;
131
- $ routeNamesBefore = array_keys ($ collection ->all ());
132
- foreach ($ this ->getAnnotations ($ method ) as $ annot ) {
133
- $ this ->addRoute ($ collection , $ annot , $ globals , $ class , $ method );
134
- if ('__invoke ' === $ method ->name ) {
152
+ if (1 === $ collection ->count () - \count ($ routeNamesBefore )) {
153
+ $ newRouteName = current (array_diff (array_keys ($ collection ->all ()), $ routeNamesBefore ));
154
+ $ collection ->addAlias (sprintf ('%s::%s ' , $ class ->name , $ method ->name ), $ newRouteName );
155
+ }
156
+ }
157
+ if (0 === $ collection ->count () && $ class ->hasMethod ('__invoke ' )) {
158
+ $ globals = $ this ->resetGlobals ();
159
+ foreach ($ this ->getAnnotations ($ class ) as $ annot ) {
160
+ $ this ->addRoute ($ collection , $ annot , $ globals , $ class , $ class ->getMethod ('__invoke ' ));
135
161
$ fqcnAlias = true ;
136
162
}
137
163
}
138
-
139
- if (1 === $ collection ->count () - \count ($ routeNamesBefore )) {
140
- $ newRouteName = current (array_diff (array_keys ($ collection ->all ()), $ routeNamesBefore ));
141
- $ collection ->addAlias (sprintf ('%s::%s ' , $ class ->name , $ method ->name ), $ newRouteName );
164
+ if ($ fqcnAlias && 1 === $ collection ->count ()) {
165
+ $ collection ->addAlias ($ class ->name , $ invokeRouteName = key ($ collection ->all ()));
166
+ $ collection ->addAlias (sprintf ('%s::__invoke ' , $ class ->name ), $ invokeRouteName );
142
167
}
143
- }
144
168
145
- if (0 === $ collection ->count () && $ class ->hasMethod ('__invoke ' )) {
146
- $ globals = $ this ->resetGlobals ();
147
- foreach ($ this ->getAnnotations ($ class ) as $ annot ) {
148
- $ this ->addRoute ($ collection , $ annot , $ globals , $ class , $ class ->getMethod ('__invoke ' ));
149
- $ fqcnAlias = true ;
169
+ if ($ this ->hasDeprecatedAnnotations ) {
170
+ trigger_deprecation ('symfony/routing ' , '6.4 ' , 'Class "%s" uses Doctrine Annotations to configure routes, which is deprecated. Use PHP attributes instead. ' , $ class ->getName ());
150
171
}
151
- }
152
-
153
- if ($ fqcnAlias && 1 === $ collection ->count ()) {
154
- $ collection ->addAlias ($ class ->name , $ invokeRouteName = key ($ collection ->all ()));
155
- $ collection ->addAlias (sprintf ('%s::__invoke ' , $ class ->name ), $ invokeRouteName );
172
+ } finally {
173
+ $ this ->hasDeprecatedAnnotations = false ;
156
174
}
157
175
158
176
return $ collection ;
@@ -279,7 +297,7 @@ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMetho
279
297
}
280
298
281
299
/**
282
- * @return array
300
+ * @return array<string, mixed>
283
301
*/
284
302
protected function getGlobals (\ReflectionClass $ class )
285
303
{
@@ -289,8 +307,8 @@ protected function getGlobals(\ReflectionClass $class)
289
307
if ($ attribute = $ class ->getAttributes ($ this ->routeAnnotationClass , \ReflectionAttribute::IS_INSTANCEOF )[0 ] ?? null ) {
290
308
$ annot = $ attribute ->newInstance ();
291
309
}
292
- if (!$ annot && $ this ->reader ) {
293
- $ annot = $ this ->reader -> getClassAnnotation ( $ class , $ this -> routeAnnotationClass ) ;
310
+ if (!$ annot && $ annot = $ this ->reader ?->getClassAnnotation( $ class , $ this -> routeAnnotationClass ) ) {
311
+ $ this ->hasDeprecatedAnnotations = true ;
294
312
}
295
313
296
314
if ($ annot ) {
@@ -377,11 +395,9 @@ protected function createRoute(string $path, array $defaults, array $requirement
377
395
abstract protected function configureRoute (Route $ route , \ReflectionClass $ class , \ReflectionMethod $ method , object $ annot );
378
396
379
397
/**
380
- * @param \ReflectionClass|\ReflectionMethod $reflection
381
- *
382
398
* @return iterable<int, RouteAnnotation>
383
399
*/
384
- private function getAnnotations (object $ reflection ): iterable
400
+ private function getAnnotations (\ ReflectionClass | \ ReflectionMethod $ reflection ): iterable
385
401
{
386
402
foreach ($ reflection ->getAttributes ($ this ->routeAnnotationClass , \ReflectionAttribute::IS_INSTANCEOF ) as $ attribute ) {
387
403
yield $ attribute ->newInstance ();
@@ -397,6 +413,8 @@ private function getAnnotations(object $reflection): iterable
397
413
398
414
foreach ($ annotations as $ annotation ) {
399
415
if ($ annotation instanceof $ this ->routeAnnotationClass ) {
416
+ $ this ->hasDeprecatedAnnotations = true ;
417
+
400
418
yield $ annotation ;
401
419
}
402
420
}
0 commit comments