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..4870f1c6 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..745e9c3a --- /dev/null +++ b/src/Cli/Symfony/DependencyInjection/Extension.php @@ -0,0 +1,81 @@ +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 + ))); + } +}