Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DI] fix preloading script generation #36103

Merged
merged 1 commit into from Mar 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Symfony/Bridge/Twig/Extension/TranslationExtension.php
Expand Up @@ -24,6 +24,9 @@
use Twig\TokenParser\AbstractTokenParser;
use Twig\TwigFilter;

// Help opcache.preload discover always-needed symbols
class_exists(TranslatorInterface::class);

/**
* Provides integration of the Translation component with Twig.
*
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -23,6 +23,11 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
use Symfony\Component\Cache\DependencyInjection\CacheCollectorPass;
use Symfony\Component\Cache\DependencyInjection\CachePoolClearerPass;
use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
Expand All @@ -32,6 +37,7 @@
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Compiler\RegisterReverseContainerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\Form\DependencyInjection\FormPass;
Expand All @@ -58,6 +64,19 @@
use Symfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass;
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass;
use Symfony\Component\VarExporter\Internal\Hydrator;
use Symfony\Component\VarExporter\Internal\Registry;

// Help opcache.preload discover always-needed symbols
class_exists(ApcuAdapter::class);
class_exists(ArrayAdapter::class);
class_exists(ChainAdapter::class);
class_exists(PhpArrayAdapter::class);
class_exists(PhpFilesAdapter::class);
class_exists(Dotenv::class);
class_exists(ErrorHandler::class);
class_exists(Hydrator::class);
class_exists(Registry::class);

/**
* Bundle.
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Routing/Router.php
Expand Up @@ -20,10 +20,15 @@
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Router as BaseRouter;

// Help opcache.preload discover always-needed symbols
class_exists(RedirectableCompiledUrlMatcher::class);
class_exists(Route::class);

/**
* This Router creates the Loader only when the cache is empty.
*
Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Bundle/TwigBundle/TwigBundle.php
Expand Up @@ -20,6 +20,20 @@
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Twig\Cache\FilesystemCache;
use Twig\Extension\CoreExtension;
use Twig\Extension\EscaperExtension;
use Twig\Extension\OptimizerExtension;
use Twig\Extension\StagingExtension;
use Twig\ExtensionSet;

// Help opcache.preload discover always-needed symbols
class_exists(FilesystemCache::class);
class_exists(CoreExtension::class);
class_exists(EscaperExtension::class);
class_exists(OptimizerExtension::class);
class_exists(StagingExtension::class);
class_exists(ExtensionSet::class);

/**
* Bundle.
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/Cache/Adapter/AdapterInterface.php
Expand Up @@ -14,6 +14,9 @@
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem;

// Help opcache.preload discover always-needed symbols
class_exists(CacheItem::class);

/**
* Interface for adapters managing instances of Symfony's CacheItem.
*
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Component/DependencyInjection/Container.php
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\DependencyInjection;

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator;
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
Expand All @@ -22,6 +24,10 @@
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Contracts\Service\ResetInterface;

// Help opcache.preload discover always-needed symbols
class_exists(RewindableGenerator::class);
class_exists(ArgumentServiceLocator::class);

/**
* Container is a dependency injection container.
*
Expand Down
81 changes: 67 additions & 14 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Expand Up @@ -81,6 +81,7 @@ class PhpDumper extends Dumper
private $inlinedRequires = [];
private $circularReferences = [];
private $singleUsePrivateIds = [];
private $preload = [];
private $addThrow = false;
private $addGetService = false;
private $locatedIds = [];
Expand Down Expand Up @@ -142,6 +143,7 @@ public function dump(array $options = [])
'hot_path_tag' => 'container.hot_path',
'inline_factories_parameter' => 'container.dumper.inline_factories',
'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
'preload_classes' => [],
'service_locator_tag' => 'container.service_locator',
'build_time' => time(),
], $options);
Expand Down Expand Up @@ -226,8 +228,12 @@ public function dump(array $options = [])

$proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;

if ($options['preload_classes']) {
$this->preload = array_combine($options['preload_classes'], $options['preload_classes']);
}

$code =
$this->startClass($options['class'], $baseClass, $preload).
$this->startClass($options['class'], $baseClass).
$this->addServices($services).
$this->addDeprecatedAliases().
$this->addDefaultParametersMethod()
Expand Down Expand Up @@ -302,7 +308,7 @@ public function dump(array $options = [])
$id = hash('crc32', $hash.$time);
$this->asFiles = false;

if ($preload && null !== $autoloadFile = $this->getAutoloadFile()) {
if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) {
$autoloadFile = substr($this->export($autoloadFile), 2, -1);

$code[$options['class'].'.preload.php'] = <<<EOF
Expand All @@ -320,8 +326,13 @@ public function dump(array $options = [])

EOF;

foreach ($preload as $class) {
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
foreach ($this->preload as $class) {
if (!$class || false !== strpos($class, '$')) {
continue;
}
if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) {
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class);
}
}

$code[$options['class'].'.preload.php'] .= <<<'EOF'
Expand Down Expand Up @@ -367,6 +378,7 @@ public function dump(array $options = [])
$this->circularReferences = [];
$this->locatedIds = [];
$this->exportedVariables = [];
$this->preload = [];

$unusedEnvs = [];
foreach ($this->container->getEnvCounters() as $env => $use) {
Expand Down Expand Up @@ -542,8 +554,10 @@ private function addServiceInclude(string $cId, Definition $definition): string
if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) {
$lineage = [];
foreach ($this->inlinedDefinitions as $def) {
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
$this->collectLineage($class, $lineage);
if (!$def->isDeprecated()) {
foreach ($this->getClasses($def) as $class) {
$this->collectLineage($class, $lineage);
}
}
}

Expand All @@ -552,9 +566,10 @@ private function addServiceInclude(string $cId, Definition $definition): string
&& ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
&& $this->container->has($id)
&& $this->isTrivialInstance($def = $this->container->findDefinition($id))
&& \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())
) {
$this->collectLineage($class, $lineage);
foreach ($this->getClasses($def) as $class) {
$this->collectLineage($class, $lineage);
}
}
}

Expand Down Expand Up @@ -804,6 +819,12 @@ protected function {$methodName}($lazyInitialization)

if ($definition->isDeprecated()) {
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
} else {
foreach ($this->inlinedDefinitions as $def) {
foreach ($this->getClasses($def) as $class) {
$this->preload[$class] = $class;
}
}
}

if ($this->getProxyDumper()->isProxyCandidate($definition)) {
Expand Down Expand Up @@ -960,7 +981,15 @@ private function addServices(array &$services = null): string
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
$services[$id] = $definition->isSynthetic() ? null : $this->addService($id, $definition);
if (!$definition->isSynthetic()) {
$services[$id] = $this->addService($id, $definition);
} else {
$services[$id] = null;

foreach ($this->getClasses($definition) as $class) {
$this->preload[$class] = $class;
}
}
}

foreach ($definitions as $id => $definition) {
Expand Down Expand Up @@ -1061,7 +1090,7 @@ private function addNewInstance(Definition $definition, string $return = '', str
return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail;
}

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

Expand Down Expand Up @@ -1124,7 +1153,7 @@ public function __construct()
$code .= $this->addMethodMap();
$code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : '';
$code .= $this->addAliases();
$code .= $this->addInlineRequires($preload);
$code .= $this->addInlineRequires();
$code .= <<<EOF
}

Expand Down Expand Up @@ -1324,7 +1353,7 @@ protected function {$methodNameAlias}()
return $code;
}

private function addInlineRequires(?array &$preload): string
private function addInlineRequires(): string
{
if (!$this->hotPathTag || !$this->inlineRequires) {
return '';
Expand All @@ -1342,8 +1371,7 @@ private function addInlineRequires(?array &$preload): string
$inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);

foreach ($inlinedDefinitions as $def) {
if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
$preload[$class] = $class;
foreach ($this->getClasses($def) as $class) {
$this->collectLineage($class, $lineage);
}
}
Expand Down Expand Up @@ -2065,4 +2093,29 @@ private function getAutoloadFile(): ?string

return null;
}

private function getClasses(Definition $definition): array
{
$classes = [];

while ($definition instanceof Definition) {
$classes[] = trim($definition->getClass(), '\\');
$factory = $definition->getFactory();

if (!\is_array($factory)) {
$factory = [$factory];
}

if (\is_string($factory[0])) {
if (false !== $i = strrpos($factory[0], '::')) {
$factory[0] = substr($factory[0], 0, $i);
}
$classes[] = trim($factory[0], '\\');
}

$definition = $factory[0];
}

return array_filter($classes);
}
}
Expand Up @@ -531,6 +531,24 @@ class ProjectServiceContainer extends Container
}
}

[ProjectServiceContainer.preload.php] => <?php
%A

$classes = [];
$classes[] = 'Bar\FooClass';
$classes[] = 'Baz';
$classes[] = 'ConfClass';
$classes[] = 'Bar';
$classes[] = 'BazClass';
$classes[] = 'Foo';
$classes[] = 'LazyContext';
$classes[] = 'FooBarBaz';
$classes[] = 'FactoryClass';
$classes[] = 'Request';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';

%A

[ProjectServiceContainer.php] => <?php

// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
Expand Down
Expand Up @@ -536,6 +536,16 @@ class ProjectServiceContainer extends Container

$classes = [];
$classes[] = 'Bar\FooClass';
$classes[] = 'Baz';
$classes[] = 'ConfClass';
$classes[] = 'Bar';
$classes[] = 'BazClass';
$classes[] = 'Foo';
$classes[] = 'LazyContext';
$classes[] = 'FooBarBaz';
$classes[] = 'FactoryClass';
$classes[] = 'Request';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';

%A

Expand Down
Expand Up @@ -165,6 +165,16 @@ class FooClass_%s extends \Bar\FooClass implements \ProxyManager\Proxy\VirtualPr
%A
}

[ProjectServiceContainer.preload.php] => <?php
%A

$classes = [];
$classes[] = 'Bar\FooClass';
$classes[] = 'Bar\FooLazyClass';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';

%A

[ProjectServiceContainer.php] => <?php

// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
Expand Down
Expand Up @@ -83,6 +83,15 @@ class ProjectServiceContainer extends Container
}
}

[ProjectServiceContainer.preload.php] => <?php
%A

$classes = [];
$classes[] = 'Bar\FooLazyClass';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';

%A

[ProjectServiceContainer.php] => <?php

// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
Expand Down
Expand Up @@ -15,6 +15,9 @@
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;

// Help opcache.preload discover always-needed symbols
class_exists(CliDumper::class);

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/HttpFoundation/AcceptHeader.php
Expand Up @@ -11,6 +11,9 @@

namespace Symfony\Component\HttpFoundation;

// Help opcache.preload discover always-needed symbols
class_exists(AcceptHeaderItem::class);

/**
* Represents an Accept-* header.
*
Expand Down