1212namespace Symfony \Component \DependencyInjection \Compiler ;
1313
1414use Symfony \Component \DependencyInjection \Argument \IteratorArgument ;
15+ use Symfony \Component \DependencyInjection \Argument \RewindableGenerator ;
1516use Symfony \Component \DependencyInjection \Argument \ServiceClosureArgument ;
17+ use Symfony \Component \DependencyInjection \Argument \ServiceLocatorArgument ;
1618use Symfony \Component \DependencyInjection \Container ;
1719use Symfony \Component \DependencyInjection \Definition ;
1820use Symfony \Component \DependencyInjection \Exception \EnvNotFoundException ;
4042 */
4143final class CheckTypeDeclarationsPass extends AbstractRecursivePass
4244{
43- private const SCALAR_TYPES = ['int ' , 'float ' , 'bool ' , 'string ' ];
45+ private const SCALAR_TYPES = [
46+ 'int ' => true ,
47+ 'float ' => true ,
48+ 'bool ' => true ,
49+ 'string ' => true ,
50+ ];
51+
52+ private const BUILTIN_TYPES = [
53+ 'array ' => true ,
54+ 'bool ' => true ,
55+ 'callable ' => true ,
56+ 'float ' => true ,
57+ 'int ' => true ,
58+ 'iterable ' => true ,
59+ 'object ' => true ,
60+ 'string ' => true ,
61+ ];
4462
4563 private $ autoload ;
4664 private $ skippedIds ;
@@ -160,33 +178,17 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
160178 $ type = $ checkedDefinition ->getClass ();
161179 }
162180
181+ $ class = null ;
182+
163183 if ($ value instanceof Definition) {
164184 $ class = $ value ->getClass ();
165185
166- if (!$ class || (!$ this ->autoload && !class_exists ($ class , false ) && !interface_exists ($ class , false ))) {
167- return ;
168- }
169-
170- if ('callable ' === $ type && (\Closure::class === $ class || method_exists ($ class , '__invoke ' ))) {
171- return ;
172- }
173-
174- if ('iterable ' === $ type && is_subclass_of ($ class , 'Traversable ' )) {
175- return ;
176- }
177-
178- if ('object ' === $ type ) {
179- return ;
180- }
181-
182- if (is_a ($ class , $ type , true )) {
186+ if (isset (self ::BUILTIN_TYPES [strtolower ($ class )])) {
187+ $ class = strtolower ($ class );
188+ } elseif (!$ class || (!$ this ->autoload && !class_exists ($ class , false ) && !interface_exists ($ class , false ))) {
183189 return ;
184190 }
185-
186- throw new InvalidParameterTypeException ($ this ->currentId , $ class , $ parameter );
187- }
188-
189- if ($ value instanceof Parameter) {
191+ } elseif ($ value instanceof Parameter) {
190192 $ value = $ this ->container ->getParameter ($ value );
191193 } elseif ($ value instanceof Expression) {
192194 $ value = $ this ->getExpressionLanguage ()->evaluate ($ value , ['container ' => $ this ->container ]);
@@ -212,30 +214,53 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
212214 return ;
213215 }
214216
215- if (\in_array ($ type , self ::SCALAR_TYPES , true ) && is_scalar ($ value )) {
217+ if (null === $ class ) {
218+ if ($ value instanceof IteratorArgument) {
219+ $ class = RewindableGenerator::class;
220+ } elseif ($ value instanceof ServiceClosureArgument) {
221+ $ class = \Closure::class;
222+ } elseif ($ value instanceof ServiceLocatorArgument) {
223+ $ class = ServiceLocator::class;
224+ } elseif (\is_object ($ value )) {
225+ $ class = \get_class ($ value );
226+ } else {
227+ $ class = \gettype ($ value );
228+ $ class = ['integer ' => 'int ' , 'double ' => 'float ' , 'boolean ' => 'bool ' ][$ class ] ?? $ class ;
229+ }
230+ }
231+
232+ if (isset (self ::SCALAR_TYPES [$ type ]) && isset (self ::SCALAR_TYPES [$ class ])) {
233+ return ;
234+ }
235+
236+ if ('string ' === $ type && \is_callable ([$ class , '__toString ' ])) {
237+ return ;
238+ }
239+
240+ if ('callable ' === $ type && (\Closure::class === $ class || \is_callable ([$ class , '__invoke ' ]))) {
216241 return ;
217242 }
218243
219- if ('callable ' === $ type && \is_array ($ value ) && isset ($ value [0 ]) && ($ value [0 ] instanceof Reference || $ value [0 ] instanceof Definition)) {
244+ if ('callable ' === $ type && \is_array ($ value ) && isset ($ value [0 ]) && ($ value [0 ] instanceof Reference || $ value [0 ] instanceof Definition || \is_string ( $ value [ 0 ]) )) {
220245 return ;
221246 }
222247
223- if (\in_array ( $ type , [ ' callable ' , ' Closure ' ], true ) && $ value instanceof ServiceClosureArgument ) {
248+ if (' iterable ' === $ type && ( \is_array ( $ value) || is_subclass_of ( $ class , \Traversable::class)) ) {
224249 return ;
225250 }
226251
227- if ('iterable ' === $ type && ( \is_array ( $ value ) || $ value instanceof \Traversable || $ value instanceof IteratorArgument )) {
252+ if ('object ' === $ type && ! isset ( self :: BUILTIN_TYPES [ $ class ] )) {
228253 return ;
229254 }
230255
231- if (' Traversable ' === $ type && ( $ value instanceof \Traversable || $ value instanceof IteratorArgument )) {
256+ if (is_a ( $ class , $ type , true )) {
232257 return ;
233258 }
234259
235260 $ checkFunction = sprintf ('is_%s ' , $ parameter ->getType ()->getName ());
236261
237262 if (!$ parameter ->getType ()->isBuiltin () || !$ checkFunction ($ value )) {
238- throw new InvalidParameterTypeException ($ this ->currentId , \is_object ($ value ) ? \get_class ( $ value ) : \gettype ($ value ), $ parameter );
263+ throw new InvalidParameterTypeException ($ this ->currentId , \is_object ($ value ) ? $ class : \gettype ($ value ), $ parameter );
239264 }
240265 }
241266
0 commit comments