bug #36103 [DI] fix preloading script generation (nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[DI] fix preloading script generation

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

(fabbot failure is a false positive)

On master, we should work on being able to preload more classes (esp. all cache-warmup artifacts).

But for 4.4, this is good enough. Submitted as a bug fix because 1. the current code that deals with preloading kinda-works, but only on "dev" mode... and 2. fixing it provides a nice boost!

Small bench on a hello world:
- before: 380 req/s
- after: 580 req/s

That's +50%!

Pro-tip: adding a few `class_exists()` as done in this PR for the classes that are always used in the implementations (e.g. `new Foo()` in the constructor) will help the preload-script generator to work optimally. Without them, it will discover the symbols to preload only if they're found on methods.

Some of those `class_exists()` are mandatory, in relation to anonymous classes and https://bugs.php.net/79349

Commits
-------

a10fc4da5d [DI] fix preloading script generation
This commit is contained in:
Fabien Potencier 2020-03-18 08:51:32 +01:00
commit e457b24ea7
30 changed files with 245 additions and 14 deletions

View File

@ -24,6 +24,9 @@ use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\AbstractTokenParser; use Twig\TokenParser\AbstractTokenParser;
use Twig\TwigFilter; use Twig\TwigFilter;
// Help opcache.preload discover always-needed symbols
class_exists(TranslatorInterface::class);
/** /**
* Provides integration of the Translation component with Twig. * Provides integration of the Translation component with Twig.
* *

View File

@ -23,6 +23,11 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceConta
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; 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\CacheCollectorPass;
use Symfony\Component\Cache\DependencyInjection\CachePoolClearerPass; use Symfony\Component\Cache\DependencyInjection\CachePoolClearerPass;
use Symfony\Component\Cache\DependencyInjection\CachePoolPass; use Symfony\Component\Cache\DependencyInjection\CachePoolPass;
@ -32,6 +37,7 @@ use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Compiler\RegisterReverseContainerPass; use Symfony\Component\DependencyInjection\Compiler\RegisterReverseContainerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\ErrorHandler\ErrorHandler; use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\Form\DependencyInjection\FormPass; use Symfony\Component\Form\DependencyInjection\FormPass;
@ -58,6 +64,19 @@ use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
use Symfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass; use Symfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass;
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass; 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. * Bundle.

View File

@ -20,10 +20,15 @@ use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainer
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Router as BaseRouter; 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. * This Router creates the Loader only when the cache is empty.
* *

View File

@ -20,6 +20,20 @@ use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle; 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. * Bundle.

View File

@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem; 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. * Interface for adapters managing instances of Symfony's CacheItem.
* *

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\DependencyInjection; 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\EnvNotFoundException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
@ -22,6 +24,10 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Contracts\Service\ResetInterface; 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. * Container is a dependency injection container.
* *

View File

@ -81,6 +81,7 @@ class PhpDumper extends Dumper
private $inlinedRequires = []; private $inlinedRequires = [];
private $circularReferences = []; private $circularReferences = [];
private $singleUsePrivateIds = []; private $singleUsePrivateIds = [];
private $preload = [];
private $addThrow = false; private $addThrow = false;
private $addGetService = false; private $addGetService = false;
private $locatedIds = []; private $locatedIds = [];
@ -142,6 +143,7 @@ class PhpDumper extends Dumper
'hot_path_tag' => 'container.hot_path', 'hot_path_tag' => 'container.hot_path',
'inline_factories_parameter' => 'container.dumper.inline_factories', 'inline_factories_parameter' => 'container.dumper.inline_factories',
'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
'preload_classes' => [],
'service_locator_tag' => 'container.service_locator', 'service_locator_tag' => 'container.service_locator',
'build_time' => time(), 'build_time' => time(),
], $options); ], $options);
@ -226,8 +228,12 @@ class PhpDumper extends Dumper
$proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null; $proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;
if ($options['preload_classes']) {
$this->preload = array_combine($options['preload_classes'], $options['preload_classes']);
}
$code = $code =
$this->startClass($options['class'], $baseClass, $preload). $this->startClass($options['class'], $baseClass).
$this->addServices($services). $this->addServices($services).
$this->addDeprecatedAliases(). $this->addDeprecatedAliases().
$this->addDefaultParametersMethod() $this->addDefaultParametersMethod()
@ -302,7 +308,7 @@ EOF;
$id = hash('crc32', $hash.$time); $id = hash('crc32', $hash.$time);
$this->asFiles = false; $this->asFiles = false;
if ($preload && null !== $autoloadFile = $this->getAutoloadFile()) { if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) {
$autoloadFile = substr($this->export($autoloadFile), 2, -1); $autoloadFile = substr($this->export($autoloadFile), 2, -1);
$code[$options['class'].'.preload.php'] = <<<EOF $code[$options['class'].'.preload.php'] = <<<EOF
@ -320,8 +326,13 @@ require __DIR__.'/Container{$hash}/{$options['class']}.php';
EOF; EOF;
foreach ($preload as $class) { foreach ($this->preload as $class) {
$code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $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' $code[$options['class'].'.preload.php'] .= <<<'EOF'
@ -367,6 +378,7 @@ EOF;
$this->circularReferences = []; $this->circularReferences = [];
$this->locatedIds = []; $this->locatedIds = [];
$this->exportedVariables = []; $this->exportedVariables = [];
$this->preload = [];
$unusedEnvs = []; $unusedEnvs = [];
foreach ($this->container->getEnvCounters() as $env => $use) { foreach ($this->container->getEnvCounters() as $env => $use) {
@ -542,8 +554,10 @@ EOF;
if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) { if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) {
$lineage = []; $lineage = [];
foreach ($this->inlinedDefinitions as $def) { foreach ($this->inlinedDefinitions as $def) {
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { if (!$def->isDeprecated()) {
$this->collectLineage($class, $lineage); foreach ($this->getClasses($def) as $class) {
$this->collectLineage($class, $lineage);
}
} }
} }
@ -552,9 +566,10 @@ EOF;
&& ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
&& $this->container->has($id) && $this->container->has($id)
&& $this->isTrivialInstance($def = $this->container->findDefinition($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);
}
} }
} }
@ -804,6 +819,12 @@ EOF;
if ($definition->isDeprecated()) { if ($definition->isDeprecated()) {
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); $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)) { if ($this->getProxyDumper()->isProxyCandidate($definition)) {
@ -960,7 +981,15 @@ EOTXT
$definitions = $this->container->getDefinitions(); $definitions = $this->container->getDefinitions();
ksort($definitions); ksort($definitions);
foreach ($definitions as $id => $definition) { 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) { foreach ($definitions as $id => $definition) {
@ -1061,7 +1090,7 @@ EOTXT
return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail; 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" : ''; $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
@ -1124,7 +1153,7 @@ EOF;
$code .= $this->addMethodMap(); $code .= $this->addMethodMap();
$code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : ''; $code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : '';
$code .= $this->addAliases(); $code .= $this->addAliases();
$code .= $this->addInlineRequires($preload); $code .= $this->addInlineRequires();
$code .= <<<EOF $code .= <<<EOF
} }
@ -1324,7 +1353,7 @@ EOF;
return $code; return $code;
} }
private function addInlineRequires(?array &$preload): string private function addInlineRequires(): string
{ {
if (!$this->hotPathTag || !$this->inlineRequires) { if (!$this->hotPathTag || !$this->inlineRequires) {
return ''; return '';
@ -1342,8 +1371,7 @@ EOF;
$inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]); $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
foreach ($inlinedDefinitions as $def) { foreach ($inlinedDefinitions as $def) {
if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { foreach ($this->getClasses($def) as $class) {
$preload[$class] = $class;
$this->collectLineage($class, $lineage); $this->collectLineage($class, $lineage);
} }
} }
@ -2065,4 +2093,29 @@ EOF;
return null; 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);
}
} }

View File

@ -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 [ProjectServiceContainer.php] => <?php
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

View File

@ -536,6 +536,16 @@ class ProjectServiceContainer extends Container
$classes = []; $classes = [];
$classes[] = 'Bar\FooClass'; $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 %A

View File

@ -165,6 +165,16 @@ class FooClass_%s extends \Bar\FooClass implements \ProxyManager\Proxy\VirtualPr
%A %A
} }
[ProjectServiceContainer.preload.php] => <?php
%A
$classes = [];
$classes[] = 'Bar\FooClass';
$classes[] = 'Bar\FooLazyClass';
$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface';
%A
[ProjectServiceContainer.php] => <?php [ProjectServiceContainer.php] => <?php
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

View File

@ -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 [ProjectServiceContainer.php] => <?php
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

View File

@ -15,6 +15,9 @@ use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\CliDumper;
// Help opcache.preload discover always-needed symbols
class_exists(CliDumper::class);
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */

View File

@ -11,6 +11,9 @@
namespace Symfony\Component\HttpFoundation; namespace Symfony\Component\HttpFoundation;
// Help opcache.preload discover always-needed symbols
class_exists(AcceptHeaderItem::class);
/** /**
* Represents an Accept-* header. * Represents an Accept-* header.
* *

View File

@ -15,6 +15,14 @@ use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
// Help opcache.preload discover always-needed symbols
class_exists(AcceptHeader::class);
class_exists(FileBag::class);
class_exists(HeaderBag::class);
class_exists(HeaderUtils::class);
class_exists(ParameterBag::class);
class_exists(ServerBag::class);
/** /**
* Request represents an HTTP request. * Request represents an HTTP request.
* *

View File

@ -11,6 +11,9 @@
namespace Symfony\Component\HttpFoundation; namespace Symfony\Component\HttpFoundation;
// Help opcache.preload discover always-needed symbols
class_exists(ResponseHeaderBag::class);
/** /**
* Response represents an HTTP response. * Response represents an HTTP response.
* *

View File

@ -18,6 +18,11 @@ use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
// Help opcache.preload discover always-needed symbols
class_exists(AttributeBag::class);
class_exists(FlashBag::class);
class_exists(SessionBagProxy::class);
/** /**
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Drak <drak@zikula.org> * @author Drak <drak@zikula.org>

View File

@ -17,6 +17,11 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandle
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
// Help opcache.preload discover always-needed symbols
class_exists(MetadataBag::class);
class_exists(StrictSessionHandler::class);
class_exists(SessionHandlerProxy::class);
/** /**
* This provides a base class for session attribute storage. * This provides a base class for session attribute storage.
* *

View File

@ -21,6 +21,9 @@ use Symfony\Component\Mime\Part\Multipart\FormDataPart;
use Symfony\Component\Mime\Part\TextPart; use Symfony\Component\Mime\Part\TextPart;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
// Help opcache.preload discover always-needed symbols
class_exists(ResponseHeaderBag::class);
/** /**
* An implementation of a Symfony HTTP kernel using a "real" HTTP client. * An implementation of a Symfony HTTP kernel using a "real" HTTP client.
* *

View File

@ -33,6 +33,18 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
// Help opcache.preload discover always-needed symbols
class_exists(LegacyEventDispatcherProxy::class);
class_exists(ControllerArgumentsEvent::class);
class_exists(ControllerEvent::class);
class_exists(ExceptionEvent::class);
class_exists(FinishRequestEvent::class);
class_exists(RequestEvent::class);
class_exists(ResponseEvent::class);
class_exists(TerminateEvent::class);
class_exists(ViewEvent::class);
class_exists(KernelEvents::class);
/** /**
* HttpKernel notifies events to convert a Request object to a Response one. * HttpKernel notifies events to convert a Request object to a Response one.
* *

View File

@ -38,6 +38,9 @@ use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
// Help opcache.preload discover always-needed symbols
class_exists(ConfigCache::class);
/** /**
* The Kernel is the heart of the Symfony system. * The Kernel is the heart of the Symfony system.
* *
@ -805,6 +808,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
'as_files' => true, 'as_files' => true,
'debug' => $this->debug, 'debug' => $this->debug,
'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(), 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(),
'preload_classes' => array_map('get_class', $this->bundles),
]); ]);
$rootCode = array_pop($content); $rootCode = array_pop($content);

View File

@ -22,6 +22,11 @@ use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
// Help opcache.preload discover always-needed symbols
class_exists(AuthenticationEvents::class);
class_exists(AuthenticationFailureEvent::class);
class_exists(AuthenticationSuccessEvent::class);
/** /**
* AuthenticationProviderManager uses a list of AuthenticationProviderInterface * AuthenticationProviderManager uses a list of AuthenticationProviderInterface
* instances to authenticate a Token. * instances to authenticate a Token.

View File

@ -14,6 +14,9 @@ namespace Symfony\Component\Security\Core\Authorization;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage;
// Help opcache.preload discover always-needed symbols
class_exists(ExpressionLanguageProvider::class);
if (!class_exists(BaseExpressionLanguage::class)) { if (!class_exists(BaseExpressionLanguage::class)) {
throw new \LogicException(sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', ExpressionLanguage::class)); throw new \LogicException(sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', ExpressionLanguage::class));
} else { } else {

View File

@ -19,6 +19,9 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
// Help opcache.preload discover always-needed symbols
class_exists(AnonymousToken::class);
/** /**
* AnonymousAuthenticationListener automatically adds a Token if none is * AnonymousAuthenticationListener automatically adds a Token if none is
* already present. * already present.

View File

@ -15,6 +15,9 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent;
// Help opcache.preload discover always-needed symbols
class_exists(RequestEvent::class);
/** /**
* @deprecated * @deprecated
* *

View File

@ -13,6 +13,9 @@ namespace Symfony\Component\Stopwatch;
use Symfony\Contracts\Service\ResetInterface; use Symfony\Contracts\Service\ResetInterface;
// Help opcache.preload discover always-needed symbols
class_exists(Section::class);
/** /**
* Stopwatch provides a way to profile code. * Stopwatch provides a way to profile code.
* *

View File

@ -16,6 +16,9 @@ use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
// Help opcache.preload discover always-needed symbols
class_exists(IntlFormatter::class);
/** /**
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com> * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
*/ */

View File

@ -35,6 +35,11 @@ use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorTrait; use Symfony\Contracts\Translation\TranslatorTrait;
// Help opcache.preload discover always-needed symbols
class_exists(TranslatorInterface::class);
class_exists(LocaleAwareInterface::class);
class_exists(TranslatorTrait::class);
/** /**
* The default implementation of {@link ValidatorBuilderInterface}. * The default implementation of {@link ValidatorBuilderInterface}.
* *

View File

@ -17,6 +17,9 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\WebLink\HttpHeaderSerializer;
// Help opcache.preload discover always-needed symbols
class_exists(HttpHeaderSerializer::class);
/** /**
* Adds the Link HTTP header to the response. * Adds the Link HTTP header to the response.
* *

View File

@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException; use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
// Help opcache.preload discover always-needed symbols
class_exists(InvalidArgumentException::class);
/** /**
* An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes. * An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes.
* *

View File

@ -14,6 +14,10 @@ namespace Symfony\Contracts\Service;
use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface; use Psr\Container\NotFoundExceptionInterface;
// Help opcache.preload discover always-needed symbols
class_exists(ContainerExceptionInterface::class);
class_exists(NotFoundExceptionInterface::class);
/** /**
* A trait to help implement ServiceProviderInterface. * A trait to help implement ServiceProviderInterface.
* *