From 120b7587518be7421d3988c9d21ceada6c4cd9be Mon Sep 17 00:00:00 2001 From: Sascha Date: Tue, 17 Dec 2024 20:28:29 +0000 Subject: [PATCH 1/2] Allow extending Symfony DI in surf --- bin/surf | 21 +++++- src/Cli/Symfony/ConsoleKernel.php | 19 ++++- .../Symfony/DependencyInjection/Extension.php | 74 +++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 src/Cli/Symfony/DependencyInjection/Extension.php diff --git a/bin/surf b/bin/surf index 823a6a57..43a0ff68 100755 --- a/bin/surf +++ b/bin/surf @@ -8,11 +8,8 @@ * file that was distributed with this source code. */ +use Custom\MyExtension; use SelfUpdate\SelfUpdateCommand; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use TYPO3\Surf\Cli\Symfony\ConsoleApplication; use TYPO3\Surf\Cli\Symfony\ConsoleKernel; @@ -20,6 +17,22 @@ requireAutoloader(); $kernel = new ConsoleKernel('prod'); +$vendorPackages = array_map( + fn($dir) => json_decode(file_get_contents($dir . '/composer/installed.json'), true)['packages'] ?? [], + array_keys(\Composer\Autoload\ClassLoader::getRegisteredLoaders()) +); +foreach ($vendorPackages as $packages) { + foreach ($packages as $package) { + if ( + 'typo3-surf-exstension' == $package['type'] + && null !== ($extension = $package['extra']['typo3-surf']['extension'] ?? null) + && class_exists($extension) + ) { + $kernel->addExtension(new $extension()); + } + } +} + $kernel->boot(); $container = $kernel->getContainer(); /** @var ConsoleApplication $application */ diff --git a/src/Cli/Symfony/ConsoleKernel.php b/src/Cli/Symfony/ConsoleKernel.php index 1b16f51b..d1f159d2 100644 --- a/src/Cli/Symfony/ConsoleKernel.php +++ b/src/Cli/Symfony/ConsoleKernel.php @@ -16,6 +16,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -26,6 +27,7 @@ use TYPO3\Surf\Cli\Symfony\CompilerPasses\CommandsToApplicationCompilerPass; use TYPO3\Surf\Domain\Service\ShellCommandService; use TYPO3\Surf\Domain\Service\ShellCommandServiceAwareInterface; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; final class ConsoleKernel { @@ -33,12 +35,18 @@ final class ConsoleKernel private ?Container $container = null; private string $environment; private ?string $projectDir = null; + private array $extensions = []; public function __construct(string $environment = 'dev') { $this->environment = $environment; } + public function addExtension(ExtensionInterface $extension): void + { + $this->extensions[] = $extension; + } + private function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(__DIR__ . '/../../../Resources/services.php'); @@ -113,11 +121,20 @@ private function initializeContainer(): void $container = new \ProjectServiceContainer(); } else { $container = new ContainerBuilder(); + $container->setParameter('kernel.environment', $this->environment); + $loader = new PhpFileLoader($container, new FileLocator()); $this->registerContainerConfiguration($loader); - $this->build($container); + foreach ($this->extensions as $extension) { + $container->registerExtension($extension); + + $container->loadFromExtension($extension->getAlias(), null); + } + $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass()); + + $this->build($container); $container->compile(); $dumper = new PhpDumper($container); diff --git a/src/Cli/Symfony/DependencyInjection/Extension.php b/src/Cli/Symfony/DependencyInjection/Extension.php new file mode 100644 index 00000000..a70c8a96 --- /dev/null +++ b/src/Cli/Symfony/DependencyInjection/Extension.php @@ -0,0 +1,74 @@ +getAlias(); + } + + public function getAlias(): string + { + $className = static::class; + if (!str_ends_with($className, 'Extension')) { + throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); + } + $classBaseName = substr(str_replace('\\', '', $className), 0, -9); + + return Container::underscore($classBaseName); + } + + public function loadExtension(ContainerConfigurator $container, ContainerBuilder $builder): void + { + $services = $container->services(); + $reflection = new \ReflectionClass($this); + $namespace = $reflection->getNamespaceName(); + $services->defaults() + ->autowire() + ->autoconfigure() + ->public(); + + $services->load($namespace . '\\', '*'); + } + + public function load(array $configs, ContainerBuilder $container): void + { + $reflection = new \ReflectionClass($this); + $file = $reflection->getFileName(); + $dirname = dirname($file); + $fileName = basename($file); + + $env = $container->getParameter('kernel.environment'); + + $extensionLoader = new PhpFileLoader($container, new FileLocator()); + $extensionLoader->setCurrentDir($dirname); + + $instanceofClosure = &\Closure::bind(fn &() => $this->instanceof, $extensionLoader, $extensionLoader)(); + + (fn(ContainerConfigurator $configurator) => $this->loadExtension($configurator, $container))((new ContainerConfigurator( + $container, + $extensionLoader, + $instanceofClosure, + $dirname, + $fileName, + $env + ))); + } +} From 370eb0e952eacf617546d3e507c2e121b838f3dc Mon Sep 17 00:00:00 2001 From: Sascha Date: Tue, 17 Dec 2024 20:48:26 +0000 Subject: [PATCH 2/2] apply php-cs --- src/Cli/Symfony/ConsoleKernel.php | 2 +- src/Cli/Symfony/DependencyInjection/Extension.php | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Cli/Symfony/ConsoleKernel.php b/src/Cli/Symfony/ConsoleKernel.php index d1f159d2..4870f1c6 100644 --- a/src/Cli/Symfony/ConsoleKernel.php +++ b/src/Cli/Symfony/ConsoleKernel.php @@ -129,7 +129,7 @@ private function initializeContainer(): void foreach ($this->extensions as $extension) { $container->registerExtension($extension); - + $container->loadFromExtension($extension->getAlias(), null); } $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass()); diff --git a/src/Cli/Symfony/DependencyInjection/Extension.php b/src/Cli/Symfony/DependencyInjection/Extension.php index a70c8a96..745e9c3a 100644 --- a/src/Cli/Symfony/DependencyInjection/Extension.php +++ b/src/Cli/Symfony/DependencyInjection/Extension.php @@ -2,6 +2,13 @@ declare(strict_types=1); +/* + * This file is part of TYPO3 Surf. + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + namespace TYPO3\Surf\Cli\Symfony\DependencyInjection; use BadMethodCallException; @@ -62,7 +69,7 @@ public function load(array $configs, ContainerBuilder $container): void $instanceofClosure = &\Closure::bind(fn &() => $this->instanceof, $extensionLoader, $extensionLoader)(); - (fn(ContainerConfigurator $configurator) => $this->loadExtension($configurator, $container))((new ContainerConfigurator( + (fn (ContainerConfigurator $configurator) => $this->loadExtension($configurator, $container))((new ContainerConfigurator( $container, $extensionLoader, $instanceofClosure,