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] add tags container.preload/.no_preload to declare extra classes to preload/services to not preload #36195

Merged
merged 1 commit into from Apr 5, 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
Expand Up @@ -32,6 +32,8 @@ class UnusedTagsPass implements CompilerPassInterface
'container.env_var_loader',
'container.env_var_processor',
'container.hot_path',
'container.no_preload',
'container.preload',
'container.reversible',
'container.service_locator',
'container.service_locator_context',
Expand Down
Expand Up @@ -434,7 +434,8 @@ public function load(array $configs, ContainerBuilder $container)
$container->registerForAutoconfiguration(CacheClearerInterface::class)
->addTag('kernel.cache_clearer');
$container->registerForAutoconfiguration(CacheWarmerInterface::class)
->addTag('kernel.cache_warmer');
->addTag('kernel.cache_warmer')
->addTag('container.no_preload');
$container->registerForAutoconfiguration(EventSubscriberInterface::class)
->addTag('kernel.event_subscriber');
$container->registerForAutoconfiguration(LocaleAwareInterface::class)
Expand Down
Expand Up @@ -34,6 +34,7 @@
</service>

<service id="annotations.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer">
<tag name="container.no_preload" />
<argument type="service" id="annotations.reader" />
<argument>%kernel.cache_dir%/annotations.php</argument>
<argument>#^Symfony\\(?:Component\\HttpKernel\\|Bundle\\FrameworkBundle\\Controller\\(?!.*Controller$))#</argument>
Expand Down
Expand Up @@ -20,6 +20,7 @@
<argument>cache.serializer</argument>
</argument>
<tag name="kernel.cache_warmer" priority="64" />
<tag name="container.no_preload" />
</service>
</services>
</container>
Expand Up @@ -11,10 +11,12 @@
<argument type="service" id="logger" on-invalid="null" />
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="console" />
<tag name="container.no_preload" />
</service>

<service id="console.suggest_missing_package_subscriber" class="Symfony\Bundle\FrameworkBundle\EventListener\SuggestMissingPackageSubscriber">
<tag name="kernel.event_subscriber" />
<tag name="container.no_preload" />
</service>

<service id="console.command.about" class="Symfony\Bundle\FrameworkBundle\Command\AboutCommand">
Expand Down
Expand Up @@ -101,6 +101,7 @@
<service id="router.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer">
<tag name="container.service_subscriber" id="router" />
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
<argument type="service" id="Psr\Container\ContainerInterface" />
</service>

Expand Down
Expand Up @@ -107,6 +107,7 @@
<argument type="collection" /><!-- Loaders injected by the extension -->
<argument>%serializer.mapping.cache.file%</argument>
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
</service>

<service id="serializer.mapping.cache.symfony" class="Psr\Cache\CacheItemPoolInterface">
Expand Down
Expand Up @@ -66,6 +66,7 @@
<service id="Symfony\Component\HttpFoundation\UrlHelper" alias="url_helper" />

<service id="cache_warmer" class="Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate" public="true">
<tag name="container.no_preload" />
<argument type="tagged_iterator" tag="kernel.cache_warmer" />
<argument>%kernel.debug%</argument>
<argument>%kernel.cache_dir%/%kernel.container_class%Deprecations.log</argument>
Expand Down
Expand Up @@ -139,6 +139,7 @@
<service id="translation.warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\TranslationsCacheWarmer">
<tag name="container.service_subscriber" id="translator" />
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
<argument type="service" id="Psr\Container\ContainerInterface" />
</service>
</services>
Expand Down
Expand Up @@ -36,6 +36,7 @@
<argument type="service" id="validator.builder" />
<argument>%validator.mapping.cache.file%</argument>
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
</service>

<service id="validator.mapping.cache.adapter" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
Expand Down
Expand Up @@ -222,6 +222,7 @@
<!-- Cache Warmers -->
<service id="security.cache_warmer.expression" class="Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer">
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
<argument type="collection" /> <!-- expressions -->
<argument type="service" id="security.expression_language" />
</service>
Expand Down
9 changes: 9 additions & 0 deletions src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml
Expand Up @@ -18,6 +18,14 @@
<argument type="service" id="twig.runtime_loader" />
</call>
<configurator service="twig.configurator.environment" method="configure" />
<tag name="container.preload" class="Twig\Cache\FilesystemCache" />
<tag name="container.preload" class="Twig\Extension\CoreExtension" />
<tag name="container.preload" class="Twig\Extension\EscaperExtension" />
<tag name="container.preload" class="Twig\Extension\OptimizerExtension" />
<tag name="container.preload" class="Twig\Extension\StagingExtension" />
<tag name="container.preload" class="Twig\ExtensionSet" />
<tag name="container.preload" class="Twig\Template" />
<tag name="container.preload" class="Twig\TemplateWrapper" />
</service>
<service id="Twig_Environment" alias="twig" />
<service id="Twig\Environment" alias="twig" />
Expand All @@ -37,6 +45,7 @@

<service id="twig.template_cache_warmer" class="Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer">
<tag name="kernel.cache_warmer" />
<tag name="container.no_preload" />
<tag name="container.service_subscriber" id="twig" />
<argument type="service" id="Psr\Container\ContainerInterface" />
<argument type="service" id="twig.template_iterator" />
Expand Down
14 changes: 0 additions & 14 deletions src/Symfony/Bundle/TwigBundle/TwigBundle.php
Expand Up @@ -19,20 +19,6 @@
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
Expand Up @@ -28,11 +28,13 @@ class AddConsoleCommandPass implements CompilerPassInterface
{
private $commandLoaderServiceId;
private $commandTag;
private $noPreloadTag;

public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command')
public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command', string $noPreloadTag = 'container.no_preload')
{
$this->commandLoaderServiceId = $commandLoaderServiceId;
$this->commandTag = $commandTag;
$this->noPreloadTag = $noPreloadTag;
}

public function process(ContainerBuilder $container)
Expand All @@ -44,6 +46,7 @@ public function process(ContainerBuilder $container)

foreach ($commandServices as $id => $tags) {
$definition = $container->getDefinition($id);
$definition->addTag($this->noPreloadTag);
$class = $container->getParameterBag()->resolveValue($definition->getClass());

if (isset($tags[0]['command'])) {
Expand Down Expand Up @@ -91,6 +94,7 @@ public function process(ContainerBuilder $container)
$container
->register($this->commandLoaderServiceId, ContainerCommandLoader::class)
->setPublic(true)
->addTag($this->noPreloadTag)
->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]);

$container->setParameter('console.command.ids', $serviceIds);
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* deprecated the `Psr\Container\ContainerInterface` and `Symfony\Component\DependencyInjection\ContainerInterface` aliases of the `service_container` service,
configure them explicitly instead
* added class `Symfony\Component\DependencyInjection\Dumper\Preloader` to help with preloading on PHP 7.4+
* added tags `container.preload`/`.no_preload` to declare extra classes to preload/services to not preload

5.0.0
-----
Expand Down
27 changes: 19 additions & 8 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Expand Up @@ -78,6 +78,7 @@ class PhpDumper extends Dumper
private $namespace;
private $asFiles;
private $hotPathTag;
private $preloadTags;
private $inlineFactories;
private $inlineRequires;
private $inlinedRequires = [];
Expand Down Expand Up @@ -143,6 +144,7 @@ public function dump(array $options = [])
'as_files' => false,
'debug' => true,
'hot_path_tag' => 'container.hot_path',
'preload_tags' => ['container.preload', 'container.no_preload'],
'inline_factories_parameter' => 'container.dumper.inline_factories',
'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
'preload_classes' => [],
Expand All @@ -154,6 +156,7 @@ public function dump(array $options = [])
$this->namespace = $options['namespace'];
$this->asFiles = $options['as_files'];
$this->hotPathTag = $options['hot_path_tag'];
$this->preloadTags = $options['preload_tags'];
$this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && (!$this->container->hasParameter($options['inline_factories_parameter']) || $this->container->getParameter($options['inline_factories_parameter']));
$this->inlineRequires = $options['inline_class_loader_parameter'] && ($this->container->hasParameter($options['inline_class_loader_parameter']) ? $this->container->getParameter($options['inline_class_loader_parameter']) : (\PHP_VERSION_ID < 70400 || $options['debug']));
$this->serviceLocatorTag = $options['service_locator_tag'];
Expand Down Expand Up @@ -571,7 +574,7 @@ private function addServiceInclude(string $cId, Definition $definition): string
$lineage = [];
foreach ($this->inlinedDefinitions as $def) {
if (!$def->isDeprecated()) {
foreach ($this->getClasses($def) as $class) {
foreach ($this->getClasses($def, $cId) as $class) {
$this->collectLineage($class, $lineage);
}
}
Expand All @@ -583,7 +586,7 @@ private function addServiceInclude(string $cId, Definition $definition): string
&& $this->container->has($id)
&& $this->isTrivialInstance($def = $this->container->findDefinition($id))
) {
foreach ($this->getClasses($def) as $class) {
foreach ($this->getClasses($def, $cId) as $class) {
$this->collectLineage($class, $lineage);
}
}
Expand Down Expand Up @@ -838,9 +841,9 @@ protected function {$methodName}($lazyInitialization)
if ($definition->isDeprecated()) {
$deprecation = $definition->getDeprecation($id);
$code .= sprintf(" trigger_deprecation(%s, %s, %s);\n\n", $this->export($deprecation['package']), $this->export($deprecation['version']), $this->export($deprecation['message']));
} else {
} elseif (!$definition->hasTag($this->preloadTags[1])) {
foreach ($this->inlinedDefinitions as $def) {
foreach ($this->getClasses($def) as $class) {
foreach ($this->getClasses($def, $id) as $class) {
$this->preload[$class] = $class;
}
}
Expand Down Expand Up @@ -1003,10 +1006,10 @@ private function addServices(array &$services = null): string
foreach ($definitions as $id => $definition) {
if (!$definition->isSynthetic()) {
$services[$id] = $this->addService($id, $definition);
} else {
} elseif (!$definition->hasTag($this->preloadTags[1])) {
$services[$id] = null;

foreach ($this->getClasses($definition) as $class) {
foreach ($this->getClasses($definition, $id) as $class) {
$this->preload[$class] = $class;
}
}
Expand Down Expand Up @@ -1385,7 +1388,7 @@ private function addInlineRequires(): string
$inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);

foreach ($inlinedDefinitions as $def) {
foreach ($this->getClasses($def) as $class) {
foreach ($this->getClasses($def, $id) as $class) {
$this->collectLineage($class, $lineage);
}
}
Expand Down Expand Up @@ -2112,11 +2115,19 @@ private function getAutoloadFile(): ?string
return null;
}

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

while ($definition instanceof Definition) {
foreach ($definition->getTag($this->preloadTags[0]) as $tag) {
if (!isset($tag['class'])) {
throw new InvalidArgumentException(sprintf('Missing attribute "class" on tag "%s" for service "%s".', $this->preloadTags[0], $id));
}

$classes[] = trim($tag['class'], '\\');
}

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

Expand Down
Expand Up @@ -134,6 +134,10 @@
->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)])
->public();
$s->set('errored_definition', 'stdClass')->private();
$s->set('preload_sidekick', 'stdClass')
->tag('container.preload', ['class' => 'Some\Sidekick1'])
->tag('container.preload', ['class' => 'Some\Sidekick2'])
->public();

$s->alias('alias_for_foo', 'foo')->private()->public();
$s->alias('alias_for_alias', ref('alias_for_foo'));
Expand Down
Expand Up @@ -187,4 +187,9 @@
$container->register('errored_definition', 'stdClass')
->addError('Service "errored_definition" is broken.');

$container->register('preload_sidekick', 'stdClass')
->setPublic(true)
->addTag('container.preload', ['class' => 'Some\Sidekick1'])
->addTag('container.preload', ['class' => 'Some\Sidekick2']);

return $container;
Expand Up @@ -36,6 +36,7 @@ digraph sc {
node_tagged_iterator [label="tagged_iterator\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_runtime_error [label="runtime_error\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_errored_definition [label="errored_definition\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_preload_sidekick [label="preload_sidekick\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"];
Expand Down
Expand Up @@ -578,6 +578,29 @@ class getNonSharedFooService extends ProjectServiceContainer
}
}

[Container%s/getPreloadSidekickService.php] => <?php

namespace Container%s;

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;

/**
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
*/
class getPreloadSidekickService extends ProjectServiceContainer
{
/**
* Gets the public 'preload_sidekick' shared service.
*
* @return \stdClass
*/
public static function do($container, $lazyLoad = true)
{
return $container->services['preload_sidekick'] = new \stdClass();
}
}

[Container%s/getRuntimeErrorService.php] => <?php

namespace Container%s;
Expand Down Expand Up @@ -731,6 +754,7 @@ class ProjectServiceContainer extends Container
'method_call1' => 'getMethodCall1Service',
'new_factory_service' => 'getNewFactoryServiceService',
'non_shared_foo' => 'getNonSharedFooService',
'preload_sidekick' => 'getPreloadSidekickService',
'runtime_error' => 'getRuntimeErrorService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
'tagged_iterator' => 'getTaggedIteratorService',
Expand Down Expand Up @@ -873,6 +897,7 @@ require __DIR__.'/Container%s/getThrowingOneService.php';
require __DIR__.'/Container%s/getTaggedIteratorService.php';
require __DIR__.'/Container%s/getServiceFromStaticMethodService.php';
require __DIR__.'/Container%s/getRuntimeErrorService.php';
require __DIR__.'/Container%s/getPreloadSidekickService.php';
require __DIR__.'/Container%s/getNonSharedFooService.php';
require __DIR__.'/Container%s/getNewFactoryServiceService.php';
require __DIR__.'/Container%s/getMethodCall1Service.php';
Expand Down Expand Up @@ -906,6 +931,8 @@ $classes[] = 'Foo';
$classes[] = 'LazyContext';
$classes[] = 'FooBarBaz';
$classes[] = 'FactoryClass';
$classes[] = 'Some\Sidekick1';
$classes[] = 'Some\Sidekick2';
$classes[] = 'Request';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';

Expand Down
Expand Up @@ -45,6 +45,7 @@ public function __construct()
'lazy_context_ignore_invalid_ref' => 'getLazyContextIgnoreInvalidRefService',
'method_call1' => 'getMethodCall1Service',
'new_factory_service' => 'getNewFactoryServiceService',
'preload_sidekick' => 'getPreloadSidekickService',
'runtime_error' => 'getRuntimeErrorService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
'tagged_iterator' => 'getTaggedIteratorService',
Expand Down Expand Up @@ -359,6 +360,16 @@ protected function getNewFactoryServiceService()
return $instance;
}

/**
* Gets the public 'preload_sidekick' shared service.
*
* @return \stdClass
*/
protected function getPreloadSidekickService()
{
return $this->services['preload_sidekick'] = new \stdClass();
}

/**
* Gets the public 'runtime_error' shared service.
*
Expand Down