1212namespace Symfony \Component \DependencyInjection \Compiler ;
1313
1414use Symfony \Component \DependencyInjection \Attribute \AutowireInline ;
15+ use Symfony \Component \DependencyInjection \ChildDefinition ;
1516use Symfony \Component \DependencyInjection \ContainerBuilder ;
1617use Symfony \Component \DependencyInjection \Definition ;
1718use Symfony \Component \DependencyInjection \Exception \RuntimeException ;
19+ use Symfony \Component \DependencyInjection \Reference ;
1820use Symfony \Component \VarExporter \ProxyHelper ;
1921
2022/**
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.
2224 *
2325 * @author Ismail Özgün Turan <oezguen.turan@dadadev.com>
2426 */
@@ -30,36 +32,110 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
3032 {
3133 $ value = parent ::processValue ($ value , $ isRoot );
3234
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 ' )) {
3436 return $ value ;
3537 }
3638
39+ $ isChildDefinition = $ value instanceof ChildDefinition;
40+
3741 try {
3842 $ constructor = $ this ->getConstructor ($ value , false );
3943 } catch (RuntimeException ) {
40- $ this ->container ->log ($ this , sprintf ('Skipping service "%s": Class or interface "%s" cannot be loaded. ' , $ this ->currentId , $ value ->getClass ()));
41-
4244 return $ value ;
4345 }
4446
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+ }
4753 }
4854
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 ();
5561
56- $ type = ProxyHelper:: exportType ( $ reflectionParameter , true );
57- $ definition = $ autowireInlineAttributeInstance -> buildDefinition ( $ autowireInlineAttributeInstance -> value , $ type , $ reflectionParameter ) ;
62+ foreach ( $ methodCalls as $ i => $ call ) {
63+ [ $ method , $ arguments ] = $ call ;
5864
59- $ this ->container ->setDefinition ('.autowire_inline. ' .ContainerBuilder::hash ($ definition ), $ definition );
65+ try {
66+ $ method = $ this ->getReflectionMethod ($ dummy , $ method );
67+ } catch (RuntimeException ) {
68+ continue ;
6069 }
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 );
6180 }
6281
6382 return $ value ;
6483 }
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+ }
65141}
0 commit comments