8000 [HttpKernel][DI] allow bundles to declare classes that should be preloaded by nicolas-grekas · Pull Request #33689 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[HttpKernel][DI] allow bundles to declare classes that should be preloaded #33689

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,37 @@ public function load(array $configs, ContainerBuilder $container)

$container->registerForAutoconfiguration(RouteLoaderInterface::class)
->addTag('routing.route_loader');

$this->addClassesToPreload([
'Psr\Log\LogLevel',
'Symfony\Component\Cache\Adapter\ApcuAdapter',
'Symfony\Component\Cache\Adapter\ArrayAdapter',
'Symfony\Component\Cache\Adapter\PhpArrayAdapter',
'Symfony\Component\Cache\Adapter\PhpFilesAdapter',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't these discovered thanks to services already ?

'Symfony\Component\Cache\CacheItem',
'Symfony\Component\DependencyInjection\Argument\RewindableGenerator',
'Symfony\Component\DependencyInjection\Argument\ServiceLocator',
'Symfony\Component\DependencyInjection\ContainerAwareInterface',
'Symfony\Component\DependencyInjection\ContainerAwareTrait',
'Symfony\Component\Dotenv\Dotenv',
'Symfony\Component\ErrorHandler\ErrorHandler',
'Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy',
'Symfony\Component\HttpFoundation\AcceptHeader',
'Symfony\Component\HttpFoundation\AcceptHeaderItem',
'Symfony\Component\HttpFoundation\FileBag',
'Symfony\Component\HttpFoundation\HeaderBag',
'Symfony\Component\HttpFoundation\HeaderUtils',
'Symfony\Component\HttpFoundation\ParameterBag',
'Symfony\Component\HttpFoundation\ResponseHeaderBag',
'Symfony\Component\HttpFoundation\ServerBag',
'Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent',
'Symfony\Component\HttpKernel\Event\ControllerEvent',
'Symfony\Component\HttpKernel\Event\TerminateEvent',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about RequestEvent and ResponseEvent ?

'Symfony\Component\HttpKernel\KernelEvents',
'Symfony\Component\VarExporter\Internal\Hydrator',
'Symfony\Component\VarExporter\Internal\Registry',
'Symfony\Component\WebLink\HttpHeaderSerializer',
]);
}

/**
Expand Down Expand Up @@ -850,6 +881,15 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co

$loader->load('routing.xml');

$this->addClassesToPreload([
'Symfony\Bundle\FrameworkBundle\Routing\RedirectableCompiledUrlMatcher',
'Symfony\Component\Routing\Annotation\Route',
'Symfony\Component\Routing\Matcher\CompiledUrlMatcher',
'Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait',
'Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface',
'Symfony\Component\Routing\Matcher\UrlMatcher',
]);

if ($config['utf8']) {
$container->getDefinition('routing.loader')->replaceArgument(2, ['utf8' => true]);
}
Expand Down Expand Up @@ -901,6 +941,14 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
{
$loader->load('session.xml');

$this->addClassesToPreload([
'Symfony\Component\HttpFoundation\Session\SessionBagProxy',
'Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler',
'Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler',
'Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy',
'Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy',
]);

// session storage
$container->setAlias('session.storage', $config['storage_id'])->setPrivate(true);
$options = ['cache_limiter' => '0'];
Expand Down Expand Up @@ -1118,6 +1166,10 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder

$loader->load('translation.xml');

$this->addClassesToPreload([
'Symfony\Component\Translation\Formatter\IntlFormatter',
]);

// Use the "real" translator instead of the identity default
$container->setAlias('translator', 'translator.default')->setPublic(true);
$container->setAlias('translator.formatter', new Alias($config['formatter'], false));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ public function load(array $configs, ContainerBuilder $container)

$container->registerForAutoconfiguration(VoterInterface::class)
->addTag('security.voter');

$this->addClassesToPreload([
'Symfony\Component\HttpFoundation\RequestMatcher',
'Symfony\Component\Security\Core\Authorization\ExpressionLanguageProvider',
'Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider',
'Symfony\Component\Security\Core\Authentication\Token\AbstractToken',
'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken',
'Symfony\Component\Security\Core\AuthenticationEvents',
'Symfony\Component\Security\Core\Event\AuthenticationEvent',
'Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent',
'Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface',
]);
}

private function createRoleHierarchy(array $config, ContainerBuilder $container)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ public function load(array $configs, ContainerBuilder $container)
$container->removeDefinition('twig.cache_warmer');
$container->removeDefinition('twig.template_cache_warmer');
}

$this->addClassesToPreload([
'Symfony\Component\Stopwatch\Section',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be added by FrameworkBundle when defining the stopwatch service instead ? Thus, I don't think we use it in prod code.

'Twig\Cache\FilesystemCache',
'Twig\Extension\CoreExtension',
'Twig\Extension\EscaperExtension',
'Twig\Extension\OptimizerExtension',
'Twig\Extension\StagingExtension',
'Twig\ExtensionSet',
'Twig\Template',
'Twig\TemplateWrapper',
]);
}

private function getBundleTemplatePaths(ContainerBuilder $container, array $config)
Expand Down
81 changes: 67 additions & 14 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,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 @@ -141,6 +142,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 @@ -225,8 +227,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 @@ -301,7 +307,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 @@ -319,8 +325,10 @@ 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 && (!(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 @@ -366,6 +374,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 @@ -541,8 +550,13 @@ 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()) {
if ($class = $this->getFactoryClass($def)) {
$this->collectLineage($class, $lineage);
}
if ($class = $def->getClass()) {
$this->collectLineage($class, $lineage);
}
}
}

Expand All @@ -551,9 +565,13 @@ 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);
if ($class = $this->getFactoryClass($def)) {
$this->collectLineage($class, $lineage);
}
if ($class = $def->getClass()) {
$this->collectLineage($class, $lineage);
}
}
}

Expand Down Expand Up @@ -798,6 +816,15 @@ 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) {
if ($class = $this->getFactoryClass($def)) {
$this->preload[$class] = $class;
}
if ($class = ltrim($def->getClass(), '\\')) {
$this->preload[$class] = $class;
}
}
}

if ($this->getProxyDumper()->isProxyCandidate($definition)) {
Expand Down Expand Up @@ -953,7 +980,18 @@ 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;

if ($class = $this->getFactoryClass($definition)) {
$this->preload[$class] = $class;
}
if ($class = ltrim($definition->getClass(), '\\')) {
$this->preload[$class] = $class;
}
}
}

foreach ($definitions as $id => $definition) {
Expand Down Expand Up @@ -1054,7 +1092,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 @@ -1117,7 +1155,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 @@ -1317,7 +1355,7 @@ protected function {$methodNameAlias}()
return $code;
}

private function addInlineRequires(?array &$preload): string
private function addInlineRequires(): string
{
if (!$this->hotPathTag || !$this->inlineRequires) {
return '';
Expand All @@ -1335,8 +1373,10 @@ 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;
if ($class = $this->getFactoryClass($def)) {
$this->collectLineage($class, $lineage);
}
if ($class = $def->getClass()) {
$this->collectLineage($class, $lineage);
}
}
Expand Down Expand Up @@ -2058,4 +2098,17 @@ private function getAutoloadFile(): ?string

return null;
}

private function getFactoryClass(Definition $definition): ?string
{
while ($definition instanceof Definition && \is_array($factory = $definition->getFactory())) {
if (\is_string($factory[0])) {
return ltrim($factory[0], '\\');
}

$definition = $factory[0];
}

return null;
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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[] = 'S F438 ymfony\Component\DependencyInjection\ContainerInterface';

%A

Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
1 change: 1 addition & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CHANGELOG
so you likely do not use those in any app based on the SF Standard or Flex edition.
* Marked all dispatched event classes as `@final`
* Added `ErrorController` to enable the preview and error rendering mechanism
* Added `Kernel::getClassesToPreload()` and `Extension::addClassesToPreload()` to hint PHP 7.4's `opcache.preload`

4.3.0
-----
Expand Down
Loading
0