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

Skip to content

Commit 4c87358

Browse files
committed
Remove non-autoloadable classes from preload
1 parent 0e56bf2 commit 4c87358

File tree

7 files changed

+136
-28
lines changed

7 files changed

+136
-28
lines changed

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

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

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: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,16 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
133133
}
134134

135135
require dirname(__DIR__, %d).'%svendor/autoload.php';
136+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
136137
require __DIR__.'/Container%s/ProjectServiceContainer.php';
137138
require __DIR__.'/Container%s/getClosureService.php';
139+
}
138140

139141
$classes = [];
140-
$classes[] = 'FooClass';
142+
$preloaded = [];
141143
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
142144

143-
Preloader::preload($classes);
145+
Preloader::preload($classes, $preloaded);
144146

145147
[ProjectServiceContainer.php] => <?php
146148

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
907907
}
908908

909909
require dirname(__DIR__, %d).'%svendor/autoload.php';
910+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
910911
require __DIR__.'/Container%s/ProjectServiceContainer.php';
911912
require __DIR__.'/Container%s/getThrowingOneService.php';
912913
require __DIR__.'/Container%s/getTaggedIteratorService.php';
@@ -932,23 +933,16 @@ require __DIR__.'/Container%s/getBazService.php';
932933
require __DIR__.'/Container%s/getBar23Service.php';
933934
require __DIR__.'/Container%s/getBAR22Service.php';
934935
require __DIR__.'/Container%s/getBAR2Service.php';
936+
}
935937

936938
$classes = [];
939+
$preloaded = [];
937940
$classes[] = 'Bar\FooClass';
938-
$classes[] = 'Baz';
939-
$classes[] = 'ConfClass';
940-
$classes[] = 'Bar';
941941
$classes[] = 'BazClass';
942-
$classes[] = 'Foo';
943942
$classes[] = 'LazyContext';
944-
$classes[] = 'FooBarBaz';
945-
$classes[] = 'FactoryClass';
946-
$classes[] = 'Some\Sidekick1';
947-
$classes[] = 'Some\Sidekick2';
948-
$classes[] = 'Request';
949943
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
950944

951-
Preloader::preload($classes);
945+
Preloader::preload($classes, $preloaded);
952946

953947
[ProjectServiceContainer.php] => <?php
954948

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -562,24 +562,18 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
562562
}
563563

564564
require dirname(__DIR__, %d).'%svendor/autoload.php';
565+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
565566
require __DIR__.'/Container%s/ProjectServiceContainer.php';
567+
}
566568

567569
$classes = [];
570+
$preloaded = [];
568571
$classes[] = 'Bar\FooClass';
569-
$classes[] = 'Baz';
570-
$classes[] = 'ConfClass';
571-
$classes[] = 'Bar';
572572
$classes[] = 'BazClass';
573-
$classes[] = 'Foo';
574573
$classes[] = 'LazyContext';
575-
$classes[] = 'FooBarBaz';
576-
$classes[] = 'FactoryClass';
577-
$classes[] = 'Some\Sidekick1';
578-
$classes[] = 'Some\Sidekick2';
579-
$classes[] = 'Request';
580574
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
581575

582-
Preloader::preload($classes);
576+
Preloader::preload($classes, $preloaded);
583577

584578
[ProjectServiceContainer.php] => <?php
585579

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,17 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
179179
}
180180

181181
require dirname(__DIR__, %d).'%svendor/autoload.php';
182+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
182183
require __DIR__.'/Container%s/ProjectServiceContainer.php';
184+
}
183185

184186
$classes = [];
187+
$preloaded = [];
185188
$classes[] = 'Bar\FooClass';
186189
$classes[] = 'Bar\FooLazyClass';
187190
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
188191

189-
Preloader::preload($classes);
192+
Preloader::preload($classes, $preloaded);
190193

191194
[ProjectServiceContainer.php] => <?php
192195

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,18 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
144144
}
145145

146146
require dirname(__DIR__, %d).'%svendor/autoload.php';
147+
if (\PHP_VERSION_ID >= 80100 || \PHP_VERSION_ID < 70400) {
147148
require __DIR__.'/Container%s/ProjectServiceContainer.php';
148149
require __DIR__.'/Container%s/proxy.php';
149150
require __DIR__.'/Container%s/getNonSharedFooService.php';
151+
}
150152

151153
$classes = [];
154+
$preloaded = [];
152155
$classes[] = 'Bar\FooLazyClass';
153156
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
154157

155-
Preloader::preload($classes);
158+
Preloader::preload($classes, $preloaded);
156159

157160
[ProjectServiceContainer.php] => <?php
158161

0 commit comments

Comments
 (0)
0