diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index aa5a2dde3d..ad16041356 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -21,6 +21,9 @@ use Twig\Extension\AbstractExtension; use Twig\NodeVisitor\NodeVisitorInterface; use Twig\TwigFilter; +// Help opcache.preload discover always-needed symbols +class_exists(TranslatorInterface::class); + /** * Provides integration of the Translation component with Twig. * diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 6cafc4399e..e21ae0d7ba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -22,6 +22,11 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceConta 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; @@ -31,6 +36,7 @@ use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; 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; @@ -57,6 +63,19 @@ use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass; 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. diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index e934e1bc21..5415d3f5ad 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -19,11 +19,16 @@ use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainer 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; use Symfony\Contracts\Service\ServiceSubscriberInterface; +// 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. * diff --git a/src/Symfony/Bundle/TwigBundle/TwigBundle.php b/src/Symfony/Bundle/TwigBundle/TwigBundle.php index 3910dd5e2e..58760b65ae 100644 --- a/src/Symfony/Bundle/TwigBundle/TwigBundle.php +++ b/src/Symfony/Bundle/TwigBundle/TwigBundle.php @@ -19,6 +19,20 @@ use Symfony\Component\Console\Application; 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. diff --git a/src/Symfony/Component/Cache/Adapter/AdapterInterface.php b/src/Symfony/Component/Cache/Adapter/AdapterInterface.php index 99c5cd1f9f..cbab77946f 100644 --- a/src/Symfony/Component/Cache/Adapter/AdapterInterface.php +++ b/src/Symfony/Component/Cache/Adapter/AdapterInterface.php @@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Adapter; 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. * diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index 2d69fe6eb6..bad2624529 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -17,7 +17,6 @@ use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -207,7 +206,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) { try { $value = $this->container->resolveEnvPlaceholders($value, true); - } catch (EnvNotFoundException | RuntimeException $e) { + } catch (\Exception $e) { // If an env placeholder cannot be resolved, we skip the validation. return; } @@ -250,7 +249,11 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass return; } - if ('iterable' === $type && (\is_array($value) || is_subclass_of($class, \Traversable::class))) { + if ('iterable' === $type && (\is_array($value) || 'array' === $class || is_subclass_of($class, \Traversable::class))) { + return; + } + + if ($type === $class) { return; } diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 53bebdfcc5..1a4a0b7053 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -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; @@ -22,6 +24,10 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; 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. * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 3b925b395b..8dbdb73090 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -82,6 +82,7 @@ class PhpDumper extends Dumper private $inlinedRequires = []; private $circularReferences = []; private $singleUsePrivateIds = []; + private $preload = []; private $addThrow = false; private $addGetService = false; private $locatedIds = []; @@ -143,6 +144,7 @@ class PhpDumper extends Dumper '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); @@ -227,8 +229,12 @@ class PhpDumper extends Dumper $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() @@ -303,7 +309,7 @@ EOF; $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'] = <<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' @@ -368,6 +379,7 @@ EOF; $this->circularReferences = []; $this->locatedIds = []; $this->exportedVariables = []; + $this->preload = []; $unusedEnvs = []; foreach ($this->container->getEnvCounters() as $env => $use) { @@ -546,8 +558,10 @@ EOF; 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); + } } } @@ -556,9 +570,10 @@ EOF; && 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); + } } } @@ -808,6 +823,12 @@ EOF; 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)) { @@ -964,7 +985,15 @@ EOTXT $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) { @@ -1065,7 +1094,7 @@ EOTXT 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" : ''; @@ -1128,7 +1157,7 @@ EOF; $code .= $this->addMethodMap(); $code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : ''; $code .= $this->addAliases(); - $code .= $this->addInlineRequires($preload); + $code .= $this->addInlineRequires(); $code .= <<hotPathTag || !$this->inlineRequires) { return ''; @@ -1346,8 +1375,7 @@ EOF; $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); } } @@ -2067,4 +2095,29 @@ EOF; 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); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index 081db17468..9524c7a4f6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -536,6 +536,42 @@ class CheckTypeDeclarationsPassTest extends TestCase $this->addToAssertionCount(1); } + public function testProcessFactoryForTypeSameAsClass() + { + $container = new ContainerBuilder(); + + $container->register('foo', Foo::class); + $container->register('bar', 'callable') + ->setFactory([ + new Reference('foo'), + 'createCallable', + ]); + $container->register('bar_call', BarMethodCall::class) + ->addMethodCall('setCallable', [new Reference('bar')]); + + (new CheckTypeDeclarationsPass(true))->process($container); + + $this->addToAssertionCount(1); + } + + public function testProcessFactoryForIterableTypeAndArrayClass() + { + $container = new ContainerBuilder(); + + $container->register('foo', Foo::class); + $container->register('bar', 'array') + ->setFactory([ + new Reference('foo'), + 'createArray', + ]); + $container->register('bar_call', BarMethodCall::class) + ->addMethodCall('setIterable', [new Reference('bar')]); + + (new CheckTypeDeclarationsPass(true))->process($container); + + $this->addToAssertionCount(1); + } + public function testProcessPassingBuiltinTypeDoesNotLoadCodeByDefault() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php index dde7afce91..34998824c5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php @@ -13,4 +13,14 @@ class Foo { return new Bar($stdClass); } + + public static function createCallable(): callable + { + return function() {}; + } + + public static function createArray(): array + { + return []; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index e3ba543f5d..9bc9764745 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -529,6 +529,24 @@ class ProjectServiceContainer extends Container } } + [ProjectServiceContainer.preload.php] => */ diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php index 90f9f479d5..057c6b530c 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -11,6 +11,9 @@ namespace Symfony\Component\HttpFoundation; +// Help opcache.preload discover always-needed symbols +class_exists(AcceptHeaderItem::class); + /** * Represents an Accept-* header. * diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 21cb15744e..b72d9a9491 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -15,6 +15,14 @@ use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; 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. * diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 4a331954bd..4bbef581e2 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -11,6 +11,9 @@ namespace Symfony\Component\HttpFoundation; +// Help opcache.preload discover always-needed symbols +class_exists(ResponseHeaderBag::class); + /** * Response represents an HTTP response. * diff --git a/src/Symfony/Component/HttpFoundation/Session/Session.php b/src/Symfony/Component/HttpFoundation/Session/Session.php index 89071b861e..8b02d2d0d9 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Session.php +++ b/src/Symfony/Component/HttpFoundation/Session/Session.php @@ -18,6 +18,11 @@ use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; 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 * @author Drak diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php index c0e0b13695..268f9784e9 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -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\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. * diff --git a/src/Symfony/Component/HttpKernel/HttpClientKernel.php b/src/Symfony/Component/HttpKernel/HttpClientKernel.php index 10bac3aff5..4d20e96916 100644 --- a/src/Symfony/Component/HttpKernel/HttpClientKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpClientKernel.php @@ -21,6 +21,9 @@ use Symfony\Component\Mime\Part\Multipart\FormDataPart; use Symfony\Component\Mime\Part\TextPart; 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. * diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 04b31835cf..850331349b 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -32,6 +32,18 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 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. * diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index aaf525de34..5b00a5cd48 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -38,6 +38,9 @@ use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; 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. * @@ -716,6 +719,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl 'as_files' => true, 'debug' => $this->debug, '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); diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php index 0c0a206293..518c21b12a 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php @@ -21,6 +21,11 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +// 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 * instances to authenticate a Token. diff --git a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php index ca15a40425..0deff425b7 100644 --- a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php +++ b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php @@ -14,6 +14,9 @@ namespace Symfony\Component\Security\Core\Authorization; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; +// Help opcache.preload discover always-needed symbols +class_exists(ExpressionLanguageProvider::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)); } else { diff --git a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php index c54e6ba67d..71c90a926e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AnonymousAuthenticationListener.php @@ -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\Exception\AuthenticationException; +// Help opcache.preload discover always-needed symbols +class_exists(AnonymousToken::class); + /** * AnonymousAuthenticationListener automatically adds a Token if none is * already present. diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 0c8abb9dbe..b1bbdeac26 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -100,10 +100,10 @@ class ContextListener extends AbstractListener if (null !== $session) { $usageIndexValue = $session instanceof Session ? $usageIndexReference = &$session->getUsageIndex() : 0; - $sessionId = $session->getId(); + $sessionId = $request->cookies->get($session->getName()); $token = $session->get($this->sessionKey); - if ($this->sessionTrackerEnabler && $session->getId() === $sessionId) { + if ($this->sessionTrackerEnabler && \in_array($sessionId, [true, $session->getId()], true)) { $usageIndexReference = $usageIndexValue; } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index 6eef745579..8cf3eeb6b6 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -344,6 +344,23 @@ class ContextListenerTest extends TestCase $this->assertNull($tokenStorage->getToken()); } + public function testWithPreviousNotStartedSession() + { + $session = new Session(new MockArraySessionStorage()); + + $request = new Request(); + $request->setSession($session); + $request->cookies->set('MOCKSESSID', true); + + $usageIndex = $session->getUsageIndex(); + + $tokenStorage = new TokenStorage(); + $listener = new ContextListener($tokenStorage, [], 'context_key', null, null, null, [$tokenStorage, 'getToken']); + $listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST)); + + $this->assertSame($usageIndex, $session->getUsageIndex()); + } + protected function runSessionOnKernelResponse($newToken, $original = null) { $session = new Session(new MockArraySessionStorage()); diff --git a/src/Symfony/Component/Stopwatch/Stopwatch.php b/src/Symfony/Component/Stopwatch/Stopwatch.php index 9a3fe56d86..8fea588040 100644 --- a/src/Symfony/Component/Stopwatch/Stopwatch.php +++ b/src/Symfony/Component/Stopwatch/Stopwatch.php @@ -13,6 +13,9 @@ namespace Symfony\Component\Stopwatch; use Symfony\Contracts\Service\ResetInterface; +// Help opcache.preload discover always-needed symbols +class_exists(Section::class); + /** * Stopwatch provides a way to profile code. * diff --git a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php index c5fd2cea64..0407964833 100644 --- a/src/Symfony/Component/Translation/Formatter/MessageFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/MessageFormatter.php @@ -14,6 +14,9 @@ namespace Symfony\Component\Translation\Formatter; use Symfony\Component\Translation\IdentityTranslator; use Symfony\Contracts\Translation\TranslatorInterface; +// Help opcache.preload discover always-needed symbols +class_exists(IntlFormatter::class); + /** * @author Abdellatif Ait boudad */ diff --git a/src/Symfony/Component/Validator/ValidatorBuilder.php b/src/Symfony/Component/Validator/ValidatorBuilder.php index 8b4318449a..e85bba34d1 100644 --- a/src/Symfony/Component/Validator/ValidatorBuilder.php +++ b/src/Symfony/Component/Validator/ValidatorBuilder.php @@ -33,6 +33,11 @@ use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; +// Help opcache.preload discover always-needed symbols +class_exists(TranslatorInterface::class); +class_exists(LocaleAwareInterface::class); +class_exists(TranslatorTrait::class); + /** * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php b/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php index 3027529a84..25faf15beb 100644 --- a/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php +++ b/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php @@ -17,6 +17,9 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\WebLink\HttpHeaderSerializer; +// Help opcache.preload discover always-needed symbols +class_exists(HttpHeaderSerializer::class); + /** * Adds the Link HTTP header to the response. * diff --git a/src/Symfony/Contracts/Cache/CacheTrait.php b/src/Symfony/Contracts/Cache/CacheTrait.php index eda3d4931a..1cc3841e2a 100644 --- a/src/Symfony/Contracts/Cache/CacheTrait.php +++ b/src/Symfony/Contracts/Cache/CacheTrait.php @@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\InvalidArgumentException; use Psr\Log\LoggerInterface; +// Help opcache.preload discover always-needed symbols +class_exists(InvalidArgumentException::class); + /** * An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes. * diff --git a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php index 4ec6eb4276..0b4d60affa 100644 --- a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php +++ b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php @@ -14,6 +14,10 @@ namespace Symfony\Contracts\Service; use Psr\Container\ContainerExceptionInterface; 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. *