@@ -90,6 +90,7 @@ class PhpDumper extends Dumper
90
90
private $ serviceLocatorTag ;
91
91
private $ exportedVariables = [];
92
92
private $ baseClass ;
93
+ private $ preloadableCache = [];
93
94
94
95
/**
95
96
* @var ProxyDumper
@@ -335,7 +336,7 @@ class %s extends {$options['class']}
335
336
if (!$ class || str_contains ($ class , '$ ' ) || \in_array ($ class , ['int ' , 'float ' , 'string ' , 'bool ' , 'resource ' , 'object ' , 'array ' , 'null ' , 'callable ' , 'iterable ' , 'mixed ' , 'void ' ], true )) {
336
337
continue ;
337
338
}
338
- if (!( class_exists ( $ class , false ) || interface_exists ( $ class , false ) || trait_exists ( $ class , false )) || ( new \ ReflectionClass ( $ class ))-> isUserDefined ( )) {
339
+ if ($ this -> isPreloadable ( $ class )) {
339
340
$ code [$ options ['class ' ].'.preload.php ' ] .= sprintf ("\$classes[] = '%s'; \n" , $ class );
340
341
}
341
342
}
@@ -398,6 +399,57 @@ class %s extends {$options['class']}
398
399
return $ code ;
399
400
}
400
401
402
+ private function isPreloadable (string $ class ): ?bool
403
+ {
404
+ if (array_key_exists ($ class , $ this ->preloadableCache )) {
405
+ return $ this ->preloadableCache [$ class ];
406
+ }
407
+ $ this ->preloadableCache [$ class ] = true ; // prevent recursion
408
+
409
+ if (!class_exists ($ class ) && !interface_exists ($ class , false ) && !trait_exists ($ class , false )) {
410
+ return $ this ->preloadableCache [$ class ] = false ;
411
+ }
412
+ $ reflection = new \ReflectionClass ($ class );
413
+ if (!$ reflection ->isUserDefined ()) {
414
+ return $ this ->preloadableCache [$ class ] = null ;
415
+ }
416
+
417
+ // Since PHP 8.1, typehinted properties does not have to be auto-loadable.
418
+ // Before PHP 8.0, user can not define typehinted properties
419
+ if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 80000 ) {
420
+ return $ this ->preloadableCache [$ class ] = true ;
421
+ }
422
+
423
+ foreach ($ reflection ->getProperties () as $ property ) {
424
+ if (null === $ type = $ property ->getType ()) {
425
+ continue ;
426
+ }
427
+ $ typesToCheck = [$ type ];
428
+ while (!empty ($ typesToCheck )) {
429
+ $ typeToCheck = array_shift ($ typesToCheck );
430
+ if ($ typeToCheck instanceof \ReflectionUnionType || $ typeToCheck instanceof \ReflectionIntersectionType) {
431
+ foreach ($ typeToCheck ->getTypes () as $ t ) {
432
+ $ typesToCheck [] = $ t ;
433
+ }
434
+
435
+ continue ;
436
+ }
437
+ if (!$ typeToCheck instanceof \ReflectionNamedType) {
438
+ continue ;
439
+ }
440
+ $ typeName = $ typeToCheck ->getName ();
441
+ if (!$ typeName || str_contains ($ typeName , '$ ' ) || \in_array ($ typeName , ['int ' , 'false ' , 'float ' , 'string ' , 'bool ' , 'resource ' , 'object ' , 'array ' , 'null ' , 'callable ' , 'iterable ' , 'mixed ' , 'void ' ], true )) {
442
+ continue ;
443
+ }
444
+ if (false === $ this ->isPreloadable ($ typeName )) {
445
+ return $ this ->preloadableCache [$ class ] = false ;
446
+ }
447
+ }
448
+ }
449
+
450
+ return $ this ->preloadableCache [$ class ] = true ;
451
+ }
452
+
401
453
/**
402
454
* Retrieves the currently set proxy dumper or instantiates one.
403
455
*/
0 commit comments