TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\DependencyInjection;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Package\Cache\PackageDependentCacheIdentifier;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ContainerBuilder
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cacheIdentifiers;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultServices;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $serviceProviderRegistryServiceName = 'service_provider_registry';
|
||||
|
||||
public function __construct(array $earlyInstances)
|
||||
{
|
||||
$this->defaultServices = $earlyInstances + [ self::class => $this ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function warmupCache(PackageManager $packageManager, FrontendInterface $cache): void
|
||||
{
|
||||
$registry = new ServiceProviderRegistry($packageManager);
|
||||
$containerBuilder = $this->buildContainer($packageManager, $registry);
|
||||
$cacheIdentifier = $this->getCacheIdentifier($packageManager);
|
||||
$this->dumpContainer($containerBuilder, $cache, $cacheIdentifier);
|
||||
}
|
||||
|
||||
public function createDependencyInjectionContainer(PackageManager $packageManager, FrontendInterface $cache, bool $failsafe = false): ContainerInterface
|
||||
{
|
||||
if (!$cache instanceof PhpFrontend) {
|
||||
throw new \RuntimeException('Cache must be instance of PhpFrontend', 1582022226);
|
||||
}
|
||||
$serviceProviderRegistry = new ServiceProviderRegistry($packageManager, $failsafe);
|
||||
|
||||
if ($failsafe) {
|
||||
return new FailsafeContainer($serviceProviderRegistry, $this->defaultServices);
|
||||
}
|
||||
|
||||
$cacheIdentifier = $this->getCacheIdentifier($packageManager);
|
||||
$containerClassName = $cacheIdentifier;
|
||||
|
||||
$hasCache = $cache->requireOnce($cacheIdentifier) !== false;
|
||||
if (!$hasCache) {
|
||||
$containerBuilder = $this->buildContainer($packageManager, $serviceProviderRegistry);
|
||||
$this->dumpContainer($containerBuilder, $cache, $cacheIdentifier);
|
||||
$cache->requireOnce($cacheIdentifier);
|
||||
}
|
||||
$container = new $containerClassName();
|
||||
|
||||
foreach ($this->defaultServices as $id => $service) {
|
||||
$container->set('_early.' . $id, $service);
|
||||
}
|
||||
|
||||
$container->set($this->serviceProviderRegistryServiceName, $serviceProviderRegistry);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
protected function buildContainer(PackageManager $packageManager, ServiceProviderRegistry $registry): SymfonyContainerBuilder
|
||||
{
|
||||
$containerBuilder = new SymfonyContainerBuilder();
|
||||
|
||||
$containerBuilder->addCompilerPass(new ResolveClassPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1000);
|
||||
$containerBuilder->addCompilerPass(new ServiceProviderCompilationPass($registry, $this->serviceProviderRegistryServiceName));
|
||||
|
||||
$globalConfigDir = Environment::getConfigPath();
|
||||
// If the config folder is outside of the document root, we allow further services per-project
|
||||
// This is usually the case in composer-based installations
|
||||
if (Environment::getPublicPath() !== Environment::getProjectPath()) {
|
||||
if (file_exists($globalConfigDir . '/system/services.php')) {
|
||||
$phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system'));
|
||||
$phpFileLoader->load('services.php');
|
||||
}
|
||||
if (file_exists($globalConfigDir . '/system/services.yaml')) {
|
||||
$yamlFileLoader = new YamlFileLoader($containerBuilder, new FileLocator($globalConfigDir . '/system'));
|
||||
$yamlFileLoader->load('services.yaml');
|
||||
}
|
||||
}
|
||||
|
||||
$packages = $packageManager->getActivePackages();
|
||||
foreach ($packages as $package) {
|
||||
$diConfigDir = $package->getPackagePath() . 'Configuration/';
|
||||
if (file_exists($diConfigDir . 'Services.php')) {
|
||||
$phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator($diConfigDir));
|
||||
$phpFileLoader->load('Services.php');
|
||||
}
|
||||
if (file_exists($diConfigDir . 'Services.yaml')) {
|
||||
$yamlFileLoader = new YamlFileLoader($containerBuilder, new FileLocator($diConfigDir));
|
||||
$yamlFileLoader->load('Services.yaml');
|
||||
}
|
||||
}
|
||||
// Store defaults entries in the DIC container
|
||||
// We need to use a workaround using aliases for synthetic services
|
||||
// But that's common in symfony (same technique is used to provide the
|
||||
// Symfony container interface as well).
|
||||
foreach (array_keys($this->defaultServices) as $id) {
|
||||
$syntheticId = '_early.' . $id;
|
||||
$containerBuilder->register($syntheticId)->setSynthetic(true)->setPublic(true);
|
||||
$containerBuilder->setAlias($id, $syntheticId)->setPublic(true);
|
||||
}
|
||||
|
||||
// Optional service, set by BootService as back reference to the original bootService
|
||||
$containerBuilder->register('_early.boot-service')->setSynthetic(true)->setPublic(true);
|
||||
|
||||
$containerBuilder->compile();
|
||||
|
||||
return $containerBuilder;
|
||||
}
|
||||
|
||||
protected function dumpContainer(SymfonyContainerBuilder $containerBuilder, FrontendInterface $cache, string $cacheIdentifier): string
|
||||
{
|
||||
$containerClassName = $cacheIdentifier;
|
||||
|
||||
$phpDumper = new PhpDumper($containerBuilder);
|
||||
$code = $phpDumper->dump(['class' => $containerClassName]);
|
||||
$code = str_replace('<?php', '', $code);
|
||||
// We need to patch the generated source code to use GeneralUtility::makeInstanceForDi() instead of `new`.
|
||||
// This is ugly, but has to stay, as long as we support SingletonInstances to be created/retrieved
|
||||
// through GeneralUtility::makeInstance.
|
||||
$code = preg_replace('/new ([^\(\s]+)\(/', '\\TYPO3\\CMS\\Core\\Utility\\GeneralUtility::makeInstanceForDi(\\1::class, ', $code);
|
||||
if ($code === null) {
|
||||
throw new \RuntimeException('Could not generate container code', 1599767133);
|
||||
}
|
||||
$code = str_replace(', )', ')', $code);
|
||||
|
||||
$cache->set($cacheIdentifier, $code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal may only be used in this class or in functional tests
|
||||
*/
|
||||
public function getCacheIdentifier(PackageManager $packageManager): string
|
||||
{
|
||||
$packageManagerCacheIdentifier = $packageManager->getCacheIdentifier() ?? '';
|
||||
return $this->cacheIdentifiers[$packageManagerCacheIdentifier] ?? $this->createCacheIdentifier($packageManager, $packageManagerCacheIdentifier);
|
||||
}
|
||||
|
||||
protected function createCacheIdentifier(PackageManager $packageManager, string $additionalIdentifier): string
|
||||
{
|
||||
return $this->cacheIdentifiers[$additionalIdentifier] = (new PackageDependentCacheIdentifier($packageManager))
|
||||
->withPrefix('DependencyInjectionContainer')
|
||||
->withAdditionalHashedIdentifier('PHP-' . PHP_MAJOR_VERSION . '-' . PHP_MINOR_VERSION)
|
||||
->toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user