66
77use DI \Attribute \Inject ;
88use DI \Attribute \Injectable ;
9+ use DI \Definition \AutowireDefinition ;
910use DI \Definition \Exception \InvalidAttribute ;
1011use DI \Definition \ObjectDefinition ;
1112use DI \Definition \ObjectDefinition \MethodInjection ;
2829 */
2930class AttributeBasedAutowiring implements DefinitionSource, Autowiring
3031{
31- /**
32- * @throws InvalidAttribute
33- */
32+ // Annotations configuration flags:
33+ // enable on implicit definitions
34+ const IMPLICIT = 1 ;
35+ // enable on all autowire definitions (which are written in DI config) by default
36+ const EXPLICIT = 2 ;
37+ // read @Injectable annotations for classes
38+ const INJECTABLE = 4 ;
39+ // read @Inject annotations for properties
40+ const PROPERTIES = 8 ;
41+ // read @Inject annotations for methods' parameters
42+ const METHODS = 16 ;
43+ // all options enabled
44+ const ALL = 31 ;
45+
46+ public function __construct (private int $ flags = 0 )
47+ {
48+ $ this ->flags = $ flags > 0 ? $ flags : self ::ALL ; // all flags turned on by default
49+ }
50+
3451 public function autowire (string $ name , ObjectDefinition $ definition = null ) : ObjectDefinition |null
3552 {
3653 $ className = $ definition ? $ definition ->getClassName () : $ name ;
@@ -40,16 +57,36 @@ public function autowire(string $name, ObjectDefinition $definition = null) : Ob
4057 }
4158
4259 $ definition = $ definition ?: new ObjectDefinition ($ name );
60+ $ useAnnotations = $ definition instanceof AutowireDefinition
61+ ? ($ definition ->isUsingAnnotations () ?? ($ this ->flags & self ::EXPLICIT ))
62+ : ($ this ->flags & self ::IMPLICIT );
4363
44- $ class = new ReflectionClass ($ className );
64+ $ class = null ;
65+ if ($ useAnnotations && $ this ->flags >= self ::INJECTABLE ) {
66+ $ class = new ReflectionClass ($ className );
4567
4668 $ this ->readInjectableAttribute ($ class , $ definition );
69+ if ($ this ->flags & self ::INJECTABLE ) {
70+ $ this ->readInjectableAnnotation ($ class , $ definition );
71+ }
4772
48- // Browse the class properties looking for annotated properties
49- $ this ->readProperties ($ class , $ definition );
73+ // Browse the class properties looking for annotated properties
74+ if ($ this ->flags & self ::PROPERTIES ) {
75+ $ this ->readProperties ($ class , $ definition );
76+ }
5077
51- // Browse the object's methods looking for annotated methods
52- $ this ->readMethods ($ class , $ definition );
78+ // Browse the object's methods looking for annotated methods
79+ if ($ this ->flags & self ::METHODS ) {
80+ $ this ->readMethods ($ class , $ definition );
81+ }
82+ }
83+
84+ // constructor parameters should always be read, even if annotations are disabled (completely or i.a. for methods)
85+ // so that it behaves at least as ReflectionBasedAutowiring
86+ if (!$ useAnnotations || !($ this ->flags & self ::METHODS )) {
87+ $ class = $ class ?? new ReflectionClass ($ className );
88+ $ this ->readConstructor ($ class , $ definition );
89+ }
5390
5491 return $ definition ;
5592 }
@@ -260,4 +297,26 @@ private function readInjectableAttribute(ReflectionClass $class, ObjectDefinitio
260297 $ definition ->setLazy ($ attribute ->isLazy ());
261298 }
262299 }
300+
301+ /**
302+ * Browse the object's constructor parameters and inject dependencies.
303+ */
304+ private function readConstructor (ReflectionClass $ class , ObjectDefinition $ definition )
305+ {
306+ if (!($ constructor = $ class ->getConstructor ()) || !$ constructor ->isPublic ()) {
307+ return ;
308+ }
309+
310+ $ parameters = [];
311+ foreach ($ constructor ->getParameters () as $ index => $ parameter ) {
312+ $ entryName = $ this ->getMethodParameter ($ index , $ parameter , []);
313+
314+ if ($ entryName !== null ) {
315+ $ parameters [$ index ] = new Reference ($ entryName );
316+ }
317+ }
318+
319+ $ constructorInjection = MethodInjection::constructor ($ parameters );
320+ $ definition ->completeConstructorInjection ($ constructorInjection );
321+ }
263322}
0 commit comments