12
12
namespace Symfony \Component \DependencyInjection \Compiler ;
13
13
14
14
use Symfony \Component \DependencyInjection \Argument \IteratorArgument ;
15
+ use Symfony \Component \DependencyInjection \Argument \RewindableGenerator ;
15
16
use Symfony \Component \DependencyInjection \Argument \ServiceClosureArgument ;
17
+ use Symfony \Component \DependencyInjection \Argument \ServiceLocatorArgument ;
16
18
use Symfony \Component \DependencyInjection \Container ;
17
19
use Symfony \Component \DependencyInjection \Definition ;
18
20
use Symfony \Component \DependencyInjection \Exception \EnvNotFoundException ;
40
42
*/
41
43
final class CheckTypeDeclarationsPass extends AbstractRecursivePass
42
44
{
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
+ ];
44
62
45
63
private $ autoload ;
46
64
private $ skippedIds ;
@@ -160,33 +178,17 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
160
178
$ type = $ checkedDefinition ->getClass ();
161
179
}
162
180
181
+ $ class = null ;
182
+
163
183
if ($ value instanceof Definition) {
164
184
$ class = $ value ->getClass ();
165
185
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 ))) {
183
189
return ;
184
190
}
185
-
186
- throw new InvalidParameterTypeException ($ this ->currentId , $ class , $ parameter );
187
- }
188
-
189
- if ($ value instanceof Parameter) {
191
+ } elseif ($ value instanceof Parameter) {
190
192
$ value = $ this ->container ->getParameter ($ value );
191
193
} elseif ($ value instanceof Expression) {
192
194
$ value = $ this ->getExpressionLanguage ()->evaluate ($ value , ['container ' => $ this ->container ]);
@@ -212,30 +214,53 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
212
214
return ;
213
215
}
214
216
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 ' ]))) {
216
241
return ;
217
242
}
218
243
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 ]) )) {
220
245
return ;
221
246
}
222
247
223
- if (\in_array ( $ type , [ ' callable ' , ' Closure ' ], true ) && $ value instanceof ServiceClosureArgument ) {
248
+ if (' iterable ' === $ type && ( \is_array ( $ value) || is_subclass_of ( $ class , \Traversable::class)) ) {
224
249
return ;
225
250
}
226
251
227
- if ('iterable ' === $ type && ( \is_array ( $ value ) || $ value instanceof \Traversable || $ value instanceof IteratorArgument )) {
252
+ if ('object ' === $ type && ! isset ( self :: BUILTIN_TYPES [ $ class ] )) {
228
253
return ;
229
254
}
230
255
231
- if (' Traversable ' === $ type && ( $ value instanceof \Traversable || $ value instanceof IteratorArgument )) {
256
+ if (is_a ( $ class , $ type , true )) {
232
257
return ;
233
258
}
234
259
235
260
$ checkFunction = sprintf ('is_%s ' , $ parameter ->getType ()->getName ());
236
261
237
262
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 );
239
264
}
240
265
}
241
266
0 commit comments