12
12
namespace Symfony \Component \DependencyInjection \Compiler ;
13
13
14
14
use Symfony \Component \DependencyInjection \Attribute \AutowireInline ;
15
+ use Symfony \Component \DependencyInjection \ChildDefinition ;
15
16
use Symfony \Component \DependencyInjection \ContainerBuilder ;
16
17
use Symfony \Component \DependencyInjection \Definition ;
17
18
use Symfony \Component \DependencyInjection \Exception \RuntimeException ;
19
+ use Symfony \Component \DependencyInjection \Reference ;
18
20
use Symfony \Component \VarExporter \ProxyHelper ;
19
21
20
22
/**
21
- * Inspects existing autowired services for {@see AutowireInline} attribute and registers the definitions for reuse.
23
+ * Inspects existing autowired services for {@see AutowireInline} attributes and registers the definitions for reuse.
22
24
*
23
25
* @author Ismail Özgün Turan <[email protected] >
24
26
*/
@@ -30,36 +32,110 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
30
32
{
31
33
$ value = parent ::processValue ($ value , $ isRoot );
32
34
33
- if (!$ value instanceof Definition || !$ value ->isAutowired () || $ value ->isAbstract () || ! $ value ->getClass ( )) {
35
+ if (!$ value instanceof Definition || !$ value ->isAutowired () || ! $ value ->getClass () || $ value ->hasTag ( ' container.ignore_attributes ' )) {
34
36
return $ value ;
35
37
}
36
38
39
+ $ isChildDefinition = $ value instanceof ChildDefinition;
40
+
37
41
try {
38
42
$ constructor = $ this ->getConstructor ($ value , false );
39
43
} catch (RuntimeException ) {
40
- $ this ->container ->log ($ this , sprintf ('Skipping service "%s": Class or interface "%s" cannot be loaded. ' , $ this ->currentId , $ value ->getClass ()));
41
-
42
44
return $ value ;
43
45
}
44
46
45
- if ($ constructor === null ) {
46
- return $ value ;
47
+ if ($ constructor ) {
48
+ $ arguments = $ this ->registerAutowireInlineAttributes ($ constructor , $ value ->getArguments (), $ isChildDefinition );
49
+
50
+ if ($ arguments !== $ value ->getArguments ()) {
51
+ $ value ->setArguments ($ arguments );
52
+ }
47
53
}
48
54
49
- $ reflectionParameters = $ constructor -> getParameters () ;
50
- foreach ( $ reflectionParameters as $ reflectionParameter ) {
51
- $ autowireInlineAttributes = $ reflectionParameter -> getAttributes (AutowireInline::class, \ReflectionAttribute:: IS_INSTANCEOF );
52
- foreach ( $ autowireInlineAttributes as $ autowireInlineAttribute ) {
53
- /** @var AutowireInline $autowireInlineAttributeInstance */
54
- $ autowireInlineAttributeInstance = $ autowireInlineAttribute -> newInstance ();
55
+ $ dummy = $ value ;
56
+ while ( null === $ dummy -> getClass () && $ dummy instanceof ChildDefinition ) {
57
+ $ dummy = $ this -> container -> findDefinition ( $ dummy -> getParent () );
58
+ }
59
+
60
+ $ methodCalls = $ value -> getMethodCalls ();
55
61
56
- $ type = ProxyHelper:: exportType ( $ reflectionParameter , true );
57
- $ definition = $ autowireInlineAttributeInstance -> buildDefinition ( $ autowireInlineAttributeInstance -> value , $ type , $ reflectionParameter ) ;
62
+ foreach ( $ methodCalls as $ i => $ call ) {
63
+ [ $ method , $ arguments ] = $ call ;
58
64
59
- $ this ->container ->setDefinition ('.autowire_inline. ' .ContainerBuilder::hash ($ definition ), $ definition );
65
+ try {
66
+ $ method = $ this ->getReflectionMethod ($ dummy , $ method );
67
+ } catch (RuntimeException ) {
68
+ continue ;
60
69
}
70
+
71
+ $ arguments = $ this ->registerAutowireInlineAttributes ($ method , $ arguments , $ isChildDefinition );
72
+
73
+ if ($ arguments !== $ call [1 ]) {
74
+ $ methodCalls [$ i ][1 ] = $ arguments ;
75
+ }
76
+ }
77
+
78
+ if ($ methodCalls !== $ value ->getMethodCalls ()) {
79
+ $ value ->setMethodCalls ($ methodCalls );
61
80
}
62
81
63
82
return $ value ;
64
83
}
84
+
85
+ private function registerAutowireInlineAttributes (\ReflectionFunctionAbstract $ method , array $ arguments , bool $ isChildDefinition ): array
86
+ {
87
+ $ parameters = $ method ->getParameters ();
88
+
89
+ if ($ method ->isVariadic ()) {
90
+ array_pop ($ parameters );
91
+ }
92
+ $ dummyContainer = new ContainerBuilder ($ this ->container ->getParameterBag ());
93
+
94
+ foreach ($ parameters as $ index => $ parameter ) {
95
+ if ($ isChildDefinition ) {
96
+ $ index = 'index_ ' .$ index ;
97
+ }
98
+
99
+ $ name = '$ ' .$ parameter ->name ;
100
+ if (\array_key_exists ($ name , $ arguments )) {
101
+ $ arguments [$ index ] = $ arguments [$ name ];
102
+ unset($ arguments [$ name ]);
103
+ }
104
+ if (\array_key_exists ($ index , $ arguments ) && '' !== $ arguments [$ index ]) {
105
+ continue ;
106
+ }
107
+ if (!$ attribute = $ parameter ->getAttributes (AutowireInline::class, \ReflectionAttribute::IS_INSTANCEOF )[0 ] ?? null ) {
108
+ continue ;
109
+ }
110
+
111
+ $ type = ProxyHelper::exportType ($ parameter , true );
112
+
113
+ if (!$ type && isset ($ arguments [$ index ])) {
114
+ continue ;
115
+ }
116
+
117
+ $ attribute = $ attribute ->newInstance ();
118
+ $ definition = $ attribute ->buildDefinition ($ attribute ->value , $ type , $ parameter );
119
+
120
+ $ dummyContainer ->setDefinition ('.autowire_inline ' , $ definition );
121
+ (new ResolveParameterPlaceHoldersPass (false , false ))->process ($ dummyContainer );
122
+
123
+ $ id = '.autowire_inline. ' .ContainerBuilder::hash ([$ this ->currentId , $ method ->class ?? null , $ method ->name , (string ) $ parameter ]);
124
+
125
+ $ this ->container ->setDefinition ($ id , $ definition );
126
+ $ arguments [$ index ] = new Reference ($ id );
127
+
128
+ if ($ definition ->isAutowired ()) {
129
+ $ currentId = $ this ->currentId ;
130
+ try {
131
+ $ this ->currentId = $ id ;
132
+ $ this ->processValue ($ definition , true );
133
+ } finally {
134
+ $ this ->currentId = $ currentId ;
135
+ }
136
+ }
137
+ }
138
+
139
+ return $ arguments ;
140
+ }
65
141
}
0 commit comments