8000 Remove non-autoloadable classes from preload · symfony/symfony@f90ffb7 · GitHub
[go: up one dir, main page]

Skip to content

Commit f90ffb7

Browse files
committed
Remove non-autoloadable classes from preload
1 parent e9a7026 commit f90ffb7

File tree

5 files changed

+114
-24
lines changed

5 files changed

+114
-24
lines changed

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class PhpDumper extends Dumper
9090
private $serviceLocatorTag;
9191
private $exportedVariables = [];
9292
private $baseClass;
93+
private $preloadableCache = [];
9394

9495
/**
9596
* @var ProxyDumper
@@ -325,24 +326,31 @@ class %s extends {$options['class']}
325326
}
326327
327328
require $autoloadFile;
328-
require __DIR__.'/$preloadedFiles';
329+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
330+
require __DIR__.'/$preloadedFiles';
331+
}
329332
330333
\$classes = [];
334+
\$preloaded = [];
331335
332336
EOF;
333337

334338
foreach ($this->preload as $class) {
335339
if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void'], true)) {
336340
continue;
337341
}
338-
if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) {
342+
if ($this->isPreloadable($class)) {
339343
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
340344
}
341345
}
342346

347+
foreach ($this->collectNonPreloadable($this->preload) as $class) {
348+
$code[$options['class'].'.preload.php'] .= sprintf("\$preloaded['%s'] = true;\n", $class);
349+
}
350+
343351
$code[$options['class'].'.preload.php'] .= <<<'EOF'
344352
345-
Preloader::preload($classes);
353+
Preloader::preload($classes, $preloaded);
346354

347355
EOF;
348356
}
@@ -398,6 +406,108 @@ class %s extends {$options['class']}
398406
return $code;
399407
}
400408

409+
private function isPreloadable(string $class): ?bool
410+
{
411+
if (array_key_exists($class, $this->preloadableCache)) {
412+
return $this->preloadableCache[$class];
413+
}
414+
415+
$this->preloadableCache[$class] = true; // prevent recursion
416+
if (in_array($class, ['self', 'static', 'parent'], true)) {
417+
return $this->preloadableCache[$class] = null;
418+
}
419+
420+
if (!class_exists($class) && !interface_exists($class) && !trait_exists($class)) {
421+
return $this->preloadableCache[$class] = false;
422+
}
423+
424+
$r = new \ReflectionClass($class);
425+
if (!$r->isUserDefined()) {
426+
return $this->preloadableCache[$class] = null;
427+
}
428+
429+
// Prior to PHP 8.1, typehinted properties have to be auto-loadable.
430+
if (\PHP_VERSION_ID >= 80100) {
431+
return $this->preloadableCache[$class] = true;
432+
}
433+
434+
// Before PHP 7.4, user can not define typehinted properties
435+
if (\PHP_VERSION_ID >= 70400) {
436+
foreach ($r->getProperties() as $p) {
437+
if (!$this->isPreloadableType($p->getType())) {
438+
// do not return to warm the preloadableCache
439+
$this->preloadableCache[$class] = false;
440+
}
441+
}
442+
}
443+
444+
// code bellow does not prevent the class to be preloaded, but is here to warm the preloadableCache
445+
foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) {
446+
foreach ($m->getParameters() as $p) {
447+
if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) {
448+
$c = $p->getDefaultValueConstantName();
449+
450+
if ($i = strpos($c, '::')) {
451+
$c = substr($c, 0, $i);
452+
if (in_array($c, ['self', 'static', 'parent'], true)) {
453+
continue;
454+
}
455+
// warm the preloadableCache
456+
$this->isPreloadable($c);
457+
}
458+
}
459+
$this->isPreloadableType($p->getType());
460+
}
461+
$this->isPreloadableType($m->getReturnType());
462+
}
463+
464+
return $this->preloadableCache[$class];
465+
}
466+
467+
private function isPreloadableType(?\ReflectionType $t): bool
468+
{
469+
if (!$t) {
470+
return true;
471+
}
472+
473+
$result = true;
474+
foreach (($t instanceof \ReflectionUnionType || $t instanceof \ReflectionIntersectionType) ? $t->getTypes() : [$t] as $t) {
475+
if ($t->isBuiltin()) {
476+
continue;
477+
}
478+
$class = $t instanceof \ReflectionNamedType ? $t->getName() : (string) $t;
479+
if (false === $this->isPreloadable($class)) {
480+
$result = false;
481+
}
482+
}
483+
484+
return $result;
485+
}
486+
487+
private function collectNonPreloadable(array $seed): array
488+
{
489+
$classes = $seed;
490+
$prev = [];
491+
while ($prev !== $classes) {
492+
$prev = $classes;
493+
foreach ($classes as $c) {
494+
// warm preloadableCache
495+
$this->isPreloadable($c);
496+
}
497+
$classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
498+
}
499+
$classes = [];
500+
foreach ($this->preloadableCache as $class => $preloadable) {
501+
if ($preloadable === false) {
502+
if (class_exists($class) || interface_exists($class) || trait_exists($class)) {
503+
$classes[] = $class;
504+
}
505+
}
506+
}
507+
508+
return $classes;
509+
}
510+
401511
/**
402512
* Retrieves the currently set proxy dumper or instantiates one.
403513
*/

src/Symfony/Component/DependencyInjection/Dumper/Preloader.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static function append(string $file, array $list): void
3737
file_put_contents($file, sprintf("\n\$classes = [];\n%sPreloader::preload(\$classes);\n", implode('', $classes)), \FILE_APPEND);
3838
}
3939

40-
public static function preload(array $classes): void
40+
public static function preload(array $classes, array $preloaded = []): void
4141
{
4242
set_error_handler(function ($t, $m, $f, $l) {
4343
if (error_reporting() & $t) {
@@ -50,7 +50,6 @@ public static function preload(array $classes): void
5050
});
5151

5252
$prev = [];
53-
$preloaded = [];
5453

5554
try {
5655
while ($prev !== $classes) {

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ require __DIR__.'/Container%s/ProjectServiceContainer.php';
137137
require __DIR__.'/Container%s/getClosureService.php';
138138

139139
$classes = [];
140-
$classes[] = 'FooClass';
141140
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
142141

143142
Preloader::preload($classes);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -935,17 +935,8 @@ require __DIR__.'/Container%s/getBAR2Service.php';
935935

936936
$classes = [];
937937
$classes[] = 'Bar\FooClass';
938-
$classes[] = 'Baz';
939-
$classes[] = 'ConfClass';
940-
$classes[] = 'Bar';
941938
$classes[] = 'BazClass';
942-
$classes[] = 'Foo';
943939
$classes[] = 'LazyContext';
944-
$classes[] = 'FooBarBaz';
945-
$classes[] = 'FactoryClass';
946-
$classes[] = 'Some\Sidekick1';
947-
$classes[] = 'Some\Sidekick2';
948-
$classes[] = 'Request';
949940
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
950941

951942
Preloader::preload($classes);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -566,17 +566,8 @@ require __DIR__.'/Container%s/ProjectServiceContainer.php';
566566

567567
$classes = [];
568568
$classes[] = 'Bar\FooClass';
569-
$classes[] = 'Baz';
570-
$classes[] = 'ConfClass';
571-
$classes[] = 'Bar';
572569
$classes[] = 'BazClass';
573-
$classes[] = 'Foo';
574570
$classes[] = 'LazyContext';
575-
$classes[] = 'FooBarBaz';
576-
$classes[] = 'FactoryClass';
577-
$classes[] = 'Some\Sidekick1';
578-
$classes[] = 'Some\Sidekick2';
579-
$classes[] = 'Request';
580571
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
581572

582573
Preloader::preload($classes);

0 commit comments

Comments
 (0)
0