Skip to content

Commit 517bd3e

Browse files
[DI] generate preload.php file for PHP 7.4 in cache folder
1 parent d6ac452 commit 517bd3e

File tree

8 files changed

+171
-30
lines changed

8 files changed

+171
-30
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
4.4.0
55
-----
66

7+
* added support for opcache.preload by generating a preloading script in the cache folder
78
* added support for dumping the container in one file instead of many files
89
* deprecated support for short factories and short configurators in Yaml
910
* deprecated `tagged` in favor of `tagged_iterator`

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

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class PhpDumper extends Dumper
8282
private $locatedIds = [];
8383
private $serviceLocatorTag;
8484
private $exportedVariables = [];
85+
private $baseClass;
8586

8687
/**
8788
* @var ProxyDumper
@@ -151,11 +152,11 @@ public function dump(array $options = [])
151152

152153
if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) {
153154
$baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass);
154-
$baseClassWithNamespace = $baseClass;
155+
$this->baseClass = $baseClass;
155156
} elseif ('Container' === $baseClass) {
156-
$baseClassWithNamespace = Container::class;
157+
$this->baseClass = Container::class;
157158
} else {
158-
$baseClassWithNamespace = $baseClass;
159+
$this->baseClass = $baseClass;
159160
}
160161

161162
$this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
@@ -222,7 +223,7 @@ public function dump(array $options = [])
222223
$proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;
223224

224225
$code =
225-
$this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
226+
$this->startClass($options['class'], $baseClass, $preload).
226227
$this->addServices($services).
227228
$this->addDeprecatedAliases().
228229
$this->addDefaultParametersMethod()
@@ -296,6 +297,33 @@ public function dump(array $options = [])
296297
$time = $options['build_time'];
297298
$id = hash('crc32', $hash.$time);
298299

300+
if ($preload) {
301+
$code[$options['class'].'.preload.php'] = <<<EOF
302+
<?php
303+
304+
// This file has been auto-generated by the Symfony Dependency Injection Component
305+
// You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired
306+
307+
use Symfony\Component\DependencyInjection\Dumper\Preloader;
308+
309+
require dirname(__DIR__, 3).'/vendor/autoload.php';
310+
require __DIR__.'/Container{$hash}/{$options['class']}.php';
311+
312+
\$classes = [];
313+
314+
EOF;
315+
316+
foreach ($preload as $class) {
317+
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
318+
}
319+
320+
$code[$options['class'].'.preload.php'] .= <<<'EOF'
321+
322+
Preloader::preload($classes);
323+
324+
EOF;
325+
}
326+
299327
$code[$options['class'].'.php'] = <<<EOF
300328
<?php
301329
{$namespaceLine}
@@ -426,14 +454,16 @@ private function collectLineage(string $class, array &$lineage)
426454
if (!$r = $this->container->getReflectionClass($class, false)) {
427455
return;
428456
}
429-
if ($this->container instanceof $class) {
457+
if (is_a($class, $this->baseClass, true)) {
430458
return;
431459
}
432460
$file = $r->getFileName();
433461
if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) {
434462
return;
435463
}
436464

465+
$lineage[$class] = substr($exportedFile, 1, -1);
466+
437467
if ($parent = $r->getParentClass()) {
438468
$this->collectLineage($parent->name, $lineage);
439469
}
@@ -446,6 +476,7 @@ private function collectLineage(string $class, array &$lineage)
446476
$this->collectLineage($parent->name, $lineage);
447477
}
448478

479+
unset($lineage[$class]);
449480
$lineage[$class] = substr($exportedFile, 1, -1);
450481
}
451482

@@ -522,13 +553,17 @@ private function addServiceInclude(string $cId, Definition $definition): string
522553
}
523554

524555
foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
556+
$file = preg_replace('#^\\$this->targetDirs\[(\d++)\]#', sprintf('\dirname(__DIR__, %d + $1)', $this->asFiles), $file);
525557
$code .= sprintf(" include_once %s;\n", $file);
526558
}
527559
}
528560

529561
foreach ($this->inlinedDefinitions as $def) {
530562
if ($file = $def->getFile()) {
531-
$code .= sprintf(" include_once %s;\n", $this->dumpValue($file));
563+
$file = $this->dumpValue($file);
564+
$file = '(' === $file[0] ? substr($file, 1, -1) : $file;
565+
$file = preg_replace('#^\\$this->targetDirs\[(\d++)\]#', sprintf('\dirname(__DIR__, %d + $1)', $this->asFiles), $file);
566+
$code .= sprintf(" include_once %s;\n", $file);
532567
}
533568
}
534569

@@ -1016,7 +1051,7 @@ private function addNewInstance(Definition $definition, string $return = '', str
10161051
return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail;
10171052
}
10181053

1019-
private function startClass(string $class, string $baseClass, string $baseClassWithNamespace): string
1054+
private function startClass(string $class, string $baseClass, ?array &$preload): string
10201055
{
10211056
$namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
10221057

@@ -1064,8 +1099,8 @@ public function __construct()
10641099
$code .= " \$this->containerDir = \$containerDir;\n";
10651100
}
10661101

1067-
if (Container::class !== $baseClassWithNamespace) {
1068-
$r = $this->container->getReflectionClass($baseClassWithNamespace, false);
1102+
if (Container::class !== $this->baseClass) {
1103+
$r = $this->container->getReflectionClass($this->baseClass, false);
10691104
if (null !== $r
10701105
&& (null !== $constructor = $r->getConstructor())
10711106
&& 0 === $constructor->getNumberOfRequiredParameters()
@@ -1085,7 +1120,7 @@ public function __construct()
10851120
$code .= $this->addMethodMap();
10861121
$code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : '';
10871122
$code .= $this->addAliases();
1088-
$code .= $this->addInlineRequires();
1123+
$code .= $this->addInlineRequires($preload);
10891124
$code .= <<<EOF
10901125
}
10911126
@@ -1285,7 +1320,7 @@ protected function {$methodNameAlias}()
12851320
return $code;
12861321
}
12871322

1288-
private function addInlineRequires(): string
1323+
private function addInlineRequires(?array &$preload): string
12891324
{
12901325
if (!$this->hotPathTag || !$this->inlineRequires) {
12911326
return '';
@@ -1304,6 +1339,7 @@ private function addInlineRequires(): string
13041339

13051340
foreach ($inlinedDefinitions as $def) {
13061341
if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
1342+
$preload[$class] = $class;
13071343
$this->collectLineage($class, $lineage);
13081344
}
13091345
}
@@ -1314,11 +1350,12 @@ private function addInlineRequires(): string
13141350
foreach ($lineage as $file) {
13151351
if (!isset($this->inlinedRequires[$file])) {
13161352
$this->inlinedRequires[$file] = true;
1353+
$file = preg_replace('#^\\$this->targetDirs\[(\d++)\]#', sprintf('\dirname(__DIR__, %d + $1)', $this->asFiles), $file);
13171354
$code .= sprintf("\n include_once %s;", $file);
13181355
}
13191356
}
13201357

1321-
return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : '';
1358+
return $code ? sprintf("\n \$this->privates['service_container'] = static function () {%s\n };\n", $code) : '';
13221359
}
13231360

13241361
private function addDefaultParametersMethod(): string
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Dumper;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*
17+
* @internal
18+
*/
19+
class Preloader
20+
{
21+
public static function preload(array $classes)
22+
{
23+
set_error_handler(function ($t, $m, $f, $l) {
24+
if (error_reporting() & $t) {
25+
if (__FILE__ !== $f) {
26+
throw new \ErrorException($m, 0, $t, $f, $l);
27+
}
28+
29+
throw new \ReflectionException($m);
30+
}
31+
});
32+
33+
$prev = [];
34+
$preloaded = [];
35+
36+
try {
37+
while ($prev !== $classes) {
38+
$prev = $classes;
39+
foreach ($classes as $c) {
40+
if (isset($preloaded[$c]) || in_array($c, ['self', 'static', 'parent'], true)) {
41+
$preloaded[$c] = true;
42+
self::doPreload($c);
43+
}
44+
}
45+
$classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
46+
}
47+
} finally {
48+
restore_error_handler();
49+
}
50+
}
51+
52+
private static function doPreload(string $class)
53+
{
54+
try {
55+
$r = new \ReflectionClass($class);
56+
57+
if ($r->isInternal()) {
58+
return;
59+
}
60+
61+
$r->getConstants();
62+
$r->getDefaultProperties();
63+
64+
if (\PHP_VERSION_ID >= 70400) {
65+
foreach ($r->getProperties() as $p) {
66+
if (($t = $p->getType()) && !$t->isBuiltin()) {
67+
self::doPreload($t->getName());
68+
}
69+
}
70+
}
71+
72+
foreach ($r->getMethods() as $m) {
73+
foreach ($m->getParameters() as $p) {
74+
if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) {
75+
$c = $p->getDefaultValueConstantName();
76+
77+
if ($i = strpos($c, '::')) {
78+
self::doPreload(substr($c, 0, $i));
79+
}
80+
}
81+
82+
if (($t = $p->getType()) && !$t->isBuiltin()) {
83+
self::doPreload($t->getName());
84+
}
85+
}
86+
87+
if (($t = $m->getReturnType()) && !$t->isBuiltin()) {
88+
self::doPreload($t->getName());
89+
}
90+
}
91+
} catch (\ReflectionException $e) {
92+
// ignore missing classes
93+
}
94+
}
95+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
265265
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
266266
// Returns the public 'method_call1' shared service.
267267

268-
include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
268+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo.php';
269269

270270
$this->services['method_call1'] = $instance = new \Bar\FooClass();
271271

@@ -300,7 +300,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
300300
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
301301
// Returns the public 'non_shared_foo' service.
302302

303-
include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
303+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo.php';
304304

305305
$this->factories['non_shared_foo'] = function () {
306306
return new \Bar\FooClass();

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ class ProjectServiceContainer extends Container
9090
'decorated' => 'decorator_service_with_name',
9191
];
9292

93-
$this->privates['service_container'] = function () {
94-
include_once $this->targetDirs[0].'/Fixtures/includes/foo.php';
93+
$this->privates['service_container'] = static function () {
94+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo.php';
9595
};
9696
}
9797

@@ -287,7 +287,7 @@ class ProjectServiceContainer extends Container
287287
*/
288288
protected function getFoo_BazService()
289289
{
290-
include_once $this->targetDirs[0].'/Fixtures/includes/classes.php';
290+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/classes.php';
291291

292292
$this->services['foo.baz'] = $instance = \BazClass::getInstance();
293293

@@ -331,7 +331,7 @@ class ProjectServiceContainer extends Container
331331
*/
332332
protected function getLazyContextService()
333333
{
334-
include_once $this->targetDirs[0].'/Fixtures/includes/classes.php';
334+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/classes.php';
335335

336336
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () {
337337
yield 'k1' => ($this->services['foo.baz'] ?? $this->getFoo_BazService());
@@ -348,7 +348,7 @@ class ProjectServiceContainer extends Container
348348
*/
349349
protected function getLazyContextIgnoreInvalidRefService()
350350
{
351-
include_once $this->targetDirs[0].'/Fixtures/includes/classes.php';
351+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/classes.php';
352352

353353
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () {
354354
yield 0 => ($this->services['foo.baz'] ?? $this->getFoo_BazService());
@@ -364,7 +364,7 @@ class ProjectServiceContainer extends Container
364364
*/
365365
protected function getMethodCall1Service()
366366
{
367-
include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
367+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo.php';
368368

369369
$this->services['method_call1'] = $instance = new \Bar\FooClass();
370370

@@ -399,7 +399,7 @@ class ProjectServiceContainer extends Container
399399
*/
400400
protected function getNonSharedFooService()
401401
{
402-
include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php');
402+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo.php';
403403

404404
return new \Bar\FooClass();
405405
}
@@ -534,6 +534,14 @@ class ProjectServiceContainer extends Container
534534
}
535535
}
536536

537+
[ProjectServiceContainer.preload.php] => <?php
538+
%A
539+
540+
$classes = [];
541+
$classes[] = 'Bar\FooClass';
542+
543+
%A
544+
537545
[ProjectServiceContainer.php] => <?php
538546

539547
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class ProjectServiceContainer extends Container
9292
});
9393
}
9494

95-
include_once $this->targetDirs[0].'/Fixtures/includes/foo_lazy.php';
95+
include_once \dirname(__DIR__, 1 + 0).'/Fixtures/includes/foo_lazy.php';
9696

9797
return new \Bar\FooClass(new \Bar\FooLazyClass());
9898
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy