@@ -81,6 +81,7 @@ class PhpDumper extends Dumper
81
81
private $ locatedIds = [];
82
82
private $ serviceLocatorTag ;
83
83
private $ exportedVariables = [];
84
+ private $ baseClass ;
84
85
85
86
/**
86
87
* @var ProxyDumper
@@ -148,11 +149,11 @@ public function dump(array $options = [])
148
149
149
150
if (0 !== strpos ($ baseClass = $ options ['base_class ' ], '\\' ) && 'Container ' !== $ baseClass ) {
150
151
$ baseClass = sprintf ('%s\%s ' , $ options ['namespace ' ] ? '\\' .$ options ['namespace ' ] : '' , $ baseClass );
151
- $ baseClassWithNamespace = $ baseClass ;
152
+ $ this -> baseClass = $ baseClass ;
152
153
} elseif ('Container ' === $ baseClass ) {
153
- $ baseClassWithNamespace = Container::class;
154
+ $ this -> baseClass = Container::class;
154
155
} else {
155
- $ baseClassWithNamespace = $ baseClass ;
156
+ $ this -> baseClass = $ baseClass ;
156
157
}
157
158
158
159
$ this ->initializeMethodNamesMap ('Container ' === $ baseClass ? Container::class : $ baseClass );
@@ -216,7 +217,7 @@ public function dump(array $options = [])
216
217
}
217
218
218
219
$ code =
219
- $ this ->startClass ($ options ['class ' ], $ baseClass , $ baseClassWithNamespace ).
220
+ $ this ->startClass ($ options ['class ' ], $ baseClass , $ preload ).
220
221
$ this ->addServices ($ services ).
221
222
$ this ->addDeprecatedAliases ().
222
223
$ this ->addDefaultParametersMethod ()
@@ -277,6 +278,104 @@ public function dump(array $options = [])
277
278
$ time = $ options ['build_time ' ];
278
279
$ id = hash ('crc32 ' , $ hash .$ time );
279
280
281
+ if ($ preload ) {
282
+ $ code [$ options ['class ' ].'.preload.php ' ] = <<<EOF
283
+ <?php
284
+
285
+ // This file has been auto-generated by the Symfony Dependency Injection Component
286
+ // You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired
287
+
288
+ require dirname(__DIR__, 3).'/vendor/autoload.php';
289
+ require __DIR__.'/Container {$ hash }/ {$ options ['class ' ]}.php';
290
+
291
+ EOF ;
292
+
293
+ $ code [$ options ['class ' ].'.preload.php ' ] .= <<<'EOF'
294
+
295
+ set_error_handler(function ($t, $m, $f, $l) {
296
+ if (error_reporting() & $t) {
297
+ if (__FILE__ !== $f) {
298
+ throw new \ErrorException($m, 0, $t, $f, $l);
299
+ }
300
+
301
+ throw new \ReflectionException($m);
302
+ }
303
+ });
304
+
305
+ $preload = function ($class) use (&$preloaded, &$preload) {
306
+ if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) {
307
+ return;
308
+ }
309
+
310
+ $preloaded[$class] = true;
311
+
312
+ try {
313
+ $r = new \ReflectionClass($class);
314
+
315
+ if ($r->isInternal()) {
316
+ return;
317
+ }
318
+
319
+ $r->getConstants();
320
+ $r->getDefaultProperties();
321
+
322
+ if (\PHP_VERSION_ID >= 70400) {
323
+ foreach ($r->getProperties() as $p) {
324
+ if (($t = $p->getType()) && !$t->isBuiltin()) {
325
+ $preload($t->getName());
326
+ }
327
+ }
328
+ }
329
+
330
+ foreach ($r->getMethods() as $m) {
331
+ foreach ($m->getParameters() as $p) {
332
+ if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) {
333
+ $c = $p->getDefaultValueConstantName();
334
+
335
+ if ($i = strpos($c, '::')) {
336
+ $preload(substr($c, 0, $i));
337
+ }
338
+ }
339
+
340
+ if (($t = $p->getType()) && !$t->isBuiltin()) {
341
+ $preload($t->getName());
342
+ }
343
+ }
344
+
345
+ if (($t = $m->getReturnType()) && !$t->isBuiltin()) {
346
+ $preload($t->getName());
347
+ }
348
+ }
349
+ } catch (\ReflectionException $e) {
350
+ // ignore missing classes
351
+ }
352
+ };
353
+
354
+
355
+ EOF;
356
+
357
+ foreach ($ preload as $ class ) {
358
+ $ code [$ options ['class ' ].'.preload.php ' ] .= sprintf ("\$preload('%s'); \n" , $ class );
359
+ }
360
+
361
+ $ code [$ options ['class ' ].'.preload.php ' ] .= <<<'EOF'
362
+
363
+ $prev = [];
364
+ $classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
365
+
366
+ while ($prev !== $classes) {
367
+ $prev = $classes;
368
+ foreach ($classes as $c) {
369
+ $preload($c);
370
+ }
371
+ $classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
372
+ }
373
+
374
+ restore_error_handler();
375
+
376
+ EOF;
377
+ }
378
+
280
379
$ code [$ options ['class ' ].'.php ' ] = <<<EOF
281
380
<?php
282
381
{$ namespaceLine }
@@ -399,14 +498,16 @@ private function collectLineage($class, array &$lineage)
399
498
if (!$ r = $ this ->container ->getReflectionClass ($ class , false )) {
400
499
return ;
401
500
}
402
- if ($ this ->container instanceof $ class ) {
501
+ if (is_a ( $ class , $ this ->baseClass , true ) ) {
403
502
return ;
404
503
}
405
504
$ file = $ r ->getFileName ();
406
505
if (!$ file || $ this ->doExport ($ file ) === $ exportedFile = $ this ->export ($ file )) {
407
506
return ;
408
507
}
409
508
509
+ $ lineage [$ class ] = substr ($ exportedFile , 1 , -1 );
510
+
410
511
if ($ parent = $ r ->getParentClass ()) {
411
512
$ this ->collectLineage ($ parent ->name , $ lineage );
412
513
}
@@ -419,6 +520,7 @@ private function collectLineage($class, array &$lineage)
419
520
$ this ->collectLineage ($ parent ->name , $ lineage );
420
521
}
421
522
523
+ unset($ lineage [$ class ]);
422
524
$ lineage [$ class ] = substr ($ exportedFile , 1 , -1 );
423
525
}
424
526
@@ -474,13 +576,16 @@ private function addServiceInclude(string $cId, Definition $definition): string
474
576
}
475
577
476
578
foreach (array_diff_key (array_flip ($ lineage ), $ this ->inlinedRequires ) as $ file => $ class ) {
579
+ $ file = preg_replace ('#^ \\$this->targetDirs\[(\d++)\]# ' , sprintf ('\dirname(__DIR__, %d + $1) ' , $ this ->asFiles ), $ file );
477
580
$ code .= sprintf (" include_once %s; \n" , $ file );
478
581
}
479
582
}
480
583
481
584
foreach ($ this ->inlinedDefinitions as $ def ) {
482
585
if ($ file = $ def ->getFile ()) {
483
- $ code .= sprintf (" include_once %s; \n" , $ this ->dumpValue ($ file ));
586
+ $ file = $ this ->dumpValue ($ file );
587
+ $ file = preg_replace ('#^ \\$this->targetDirs\[(\d++)\]# ' , sprintf ('\dirname(__DIR__, %d + $1) ' , $ this ->asFiles ), $ file );
588
+ $ code .= sprintf (" include_once %s; \n" , $ file );
484
589
}
485
590
}
486
591
@@ -958,7 +1063,7 @@ private function addNewInstance(Definition $definition, string $return = '', str
958
1063
return $ return .sprintf ('new %s(%s) ' , $ this ->dumpLiteralClass ($ this ->dumpValue ($ class )), implode (', ' , $ arguments )).$ tail ;
959
1064
}
960
1065
961
- private function startClass (string $ class , string $ baseClass , string $ baseClassWithNamespace ): string
1066
+ private function startClass (string $ class , string $ baseClass , ? array & $ preload ): string
962
1067
{
963
1068
$ namespaceLine = !$ this ->asFiles && $ this ->namespace ? "\nnamespace {$ this ->namespace }; \n" : '' ;
964
1069
@@ -1005,8 +1110,8 @@ public function __construct()
1005
1110
$ code .= " \$this->containerDir = \$containerDir; \n" ;
1006
1111
}
1007
1112
1008
- if (Container::class !== $ baseClassWithNamespace ) {
1009
- $ r = $ this ->container ->getReflectionClass ($ baseClassWithNamespace , false );
1113
+ if (Container::class !== $ this -> baseClass ) {
1114
+ $ r = $ this ->container ->getReflectionClass ($ this -> baseClass , false );
1010
1115
if (null !== $ r
1011
1116
&& (null !== $ constructor = $ r ->getConstructor ())
1012
1117
&& 0 === $ constructor ->getNumberOfRequiredParameters ()
@@ -1026,7 +1131,7 @@ public function __construct()
1026
1131
$ code .= $ this ->addMethodMap ();
1027
1132
$ code .= $ this ->asFiles ? $ this ->addFileMap () : '' ;
1028
1133
$ code .= $ this ->addAliases ();
1029
- $ code .= $ this ->addInlineRequires ();
1134
+ $ code .= $ this ->addInlineRequires ($ preload );
1030
1135
$ code .= <<<EOF
1031
1136
}
1032
1137
@@ -1227,7 +1332,7 @@ protected function {$methodNameAlias}()
1227
1332
return $ code ;
1228
1333
}
1229
1334
1230
- private function addInlineRequires (): string
1335
+ private function addInlineRequires (? array & $ preload ): string
1231
1336
{
1232
1337
if (!$ this ->hotPathTag || !$ this ->inlineRequires ) {
1233
1338
return '' ;
@@ -1241,6 +1346,7 @@ private function addInlineRequires(): string
1241
1346
1242
1347
foreach ($ inlinedDefinitions as $ def ) {
1243
1348
if (\is_string ($ class = \is_array ($ factory = $ def ->getFactory ()) && \is_string ($ factory [0 ]) ? $ factory [0 ] : $ def ->getClass ())) {
1349
+ $ preload [$ class ] = $ class ;
1244
1350
$ this ->collectLineage ($ class , $ lineage );
1245
1351
}
1246
1352
}
@@ -1251,11 +1357,12 @@ private function addInlineRequires(): string
1251
1357
foreach ($ lineage as $ file ) {
1252
1358
if (!isset ($ this ->inlinedRequires [$ file ])) {
1253
1359
$ this ->inlinedRequires [$ file ] = true ;
1360
+ $ file = preg_replace ('#^ \\$this->targetDirs\[(\d++)\]# ' , sprintf ('\dirname(__DIR__, %d + $1) ' , $ this ->asFiles ), $ file );
1254
1361
$ code .= sprintf ("\n include_once %s; " , $ file );
1255
1362
}
1256
1363
}
1257
1364
1258
- return $ code ? sprintf ("\n \$this->privates['service_container'] = function () {%s \n }; \n" , $ code ) : '' ;
1365
+ return $ code ? sprintf ("\n \$this->privates['service_container'] = static function () {%s \n }; \n" , $ code ) : '' ;
1259
1366
}
1260
1367
1261
1368
private function addDefaultParametersMethod (): string
0 commit comments