From 9c9b99cc6562450410ee29022bec9b7b0afd9bf1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 8 Dec 2019 14:14:17 +0100 Subject: [PATCH] [FrameworkBundle] Allow using the kernel as a registry of controllers and service factories --- .../Kernel/MicroKernelTrait.php | 35 ++++++-- .../Tests/Kernel/MicroKernelTraitTest.php | 13 +++ .../Kernel/flex-style/config/bundles.php | 7 ++ .../flex-style/src/FlexStyleMicroKernel.php | 85 +++++++++++++++++++ .../Bundle/FrameworkBundle/composer.json | 2 +- .../Configurator/AbstractConfigurator.php | 11 ++- .../Configurator/ContainerConfigurator.php | 6 +- .../Configurator/ServicesConfigurator.php | 9 +- ...RegisterControllerArgumentLocatorsPass.php | 11 ++- ...sterControllerArgumentLocatorsPassTest.php | 16 +++- 10 files changed, 176 insertions(+), 19 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/config/bundles.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php index f4ac7e4b16174..4125a9a0d9766 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -13,9 +13,13 @@ use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\Configurator\AbstractConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader as ContainerPhpFileLoader; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +use Symfony\Component\Routing\Loader\PhpFileLoader as RoutingPhpFileLoader; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollectionBuilder; @@ -93,6 +97,8 @@ public function registerContainerConfiguration(LoaderInterface $loader) if (!$container->hasDefinition('kernel')) { $container->register('kernel', static::class) + ->addTag('controller.service_arguments') + ->setAutoconfigured(true) ->setSynthetic(true) ->setPublic(true) ; @@ -101,12 +107,9 @@ public function registerContainerConfiguration(LoaderInterface $loader) $kernelDefinition = $container->getDefinition('kernel'); $kernelDefinition->addTag('routing.route_loader'); - if ($this instanceof EventSubscriberInterface) { - $kernelDefinition->addTag('kernel.event_subscriber'); - } - $container->addObjectResource($this); $container->fileExists($this->getProjectDir().'/config/bundles.php'); + $container->setParameter('kernel.secret', '%env(APP_SECRET)%'); try { $this->configureContainer($container, $loader); @@ -120,16 +123,27 @@ public function registerContainerConfiguration(LoaderInterface $loader) } } + // the user has opted into using the ContainerConfigurator + $defaultDefinition = (new Definition())->setAutowired(true)->setAutoconfigured(true); + /* @var ContainerPhpFileLoader $kernelLoader */ $kernelLoader = $loader->getResolver()->resolve($file); $kernelLoader->setCurrentDir(\dirname($file)); $instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)(); + $valuePreProcessor = AbstractConfigurator::$valuePreProcessor; + AbstractConfigurator::$valuePreProcessor = function ($value) { + return $this === $value ? new Reference('kernel') : $value; + }; + try { - $this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file), $loader); + $this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file, $defaultDefinition), $loader); } finally { $instanceof = []; $kernelLoader->registerAliasesForSinglyImplementedInterfaces(); + AbstractConfigurator::$valuePreProcessor = $valuePreProcessor; } + + $container->setAlias(static::class, 'kernel'); }); } @@ -139,6 +153,7 @@ public function registerContainerConfiguration(LoaderInterface $loader) public function loadRoutes(LoaderInterface $loader) { $file = (new \ReflectionObject($this))->getFileName(); + /* @var RoutingPhpFileLoader $kernelLoader */ $kernelLoader = $loader->getResolver()->resolve($file); $kernelLoader->setCurrentDir(\dirname($file)); $collection = new RouteCollection(); @@ -146,6 +161,14 @@ public function loadRoutes(LoaderInterface $loader) try { $this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file)); + foreach ($collection as $route) { + $controller = $route->getDefault('_controller'); + + if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) { + $route->setDefault('_controller', ['kernel', $controller[1]]); + } + } + return $collection; } catch (\TypeError $e) { if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureRoutes() must be an instance of %s,', static::class, RouteCollectionBuilder::class))) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index dd909ea6fc8ce..3f61496bc2574 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -17,6 +17,8 @@ use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\HttpFoundation\Request; +require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php'; + class MicroKernelTraitTest extends TestCase { public function test() @@ -56,4 +58,15 @@ public function testRoutingRouteLoaderTagIsAdded() $kernel->registerContainerConfiguration(new ClosureLoader($container)); $this->assertTrue($container->getDefinition('kernel')->hasTag('routing.route_loader')); } + + public function testFlexStyle() + { + $kernel = new FlexStyleMicroKernel('test', false); + $kernel->boot(); + + $request = Request::create('/'); + $response = $kernel->handle($request); + + $this->assertEquals('Have a great day!', $response->getContent()); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/config/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/config/bundles.php new file mode 100644 index 0000000000000..0691b2b32d19c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/config/bundles.php @@ -0,0 +1,7 @@ + ['all' => true], +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php new file mode 100644 index 0000000000000..016c66f612c2b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + +class FlexStyleMicroKernel extends Kernel +{ + use MicroKernelTrait; + + private $cacheDir; + + public function halloweenAction(\stdClass $o) + { + return new Response($o->halloween); + } + + public function createHalloween(LoggerInterface $logger, string $halloween) + { + $o = new \stdClass(); + $o->logger = $logger; + $o->halloween = $halloween; + + return $o; + } + + public function getCacheDir(): string + { + return $this->cacheDir = sys_get_temp_dir().'/sf_flex_kernel'; + } + + public function getLogDir(): string + { + return $this->cacheDir; + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + $fs = new Filesystem(); + $fs->remove($this->cacheDir); + } + + protected function configureRoutes(RoutingConfigurator $routes): void + { + $routes->add('halloween', '/')->controller([$this, 'halloweenAction']); + } + + protected function configureContainer(ContainerConfigurator $c) + { + $c->parameters() + ->set('halloween', 'Have a great day!'); + + $c->services() + ->set('logger', NullLogger::class) + ->set('stdClass', 'stdClass') + ->factory([$this, 'createHalloween']) + ->arg('$halloween', '%halloween%'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 46183969a8457..896d149b10097 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -20,7 +20,7 @@ "ext-xml": "*", "symfony/cache": "^4.4|^5.0", "symfony/config": "^5.0", - "symfony/dependency-injection": "^5.0.1", + "symfony/dependency-injection": "^5.1", "symfony/error-handler": "^4.4.1|^5.0.1", "symfony/http-foundation": "^4.4|^5.0", "symfony/http-kernel": "^5.0", diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php index 539eb3914d1e1..db0b488426b51 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php @@ -22,6 +22,11 @@ abstract class AbstractConfigurator { const FACTORY = 'unknown'; + /** + * @var callable(mixed $value, bool $allowService)|null + */ + public static $valuePreProcessor; + /** @internal */ protected $definition; @@ -49,7 +54,11 @@ public static function processValue($value, $allowServices = false) $value[$k] = static::processValue($v, $allowServices); } - return $value; + return self::$valuePreProcessor ? (self::$valuePreProcessor)($value, $allowServices) : $value; + } + + if (self::$valuePreProcessor) { + $value = (self::$valuePreProcessor)($value, $allowServices); } if ($value instanceof ReferenceConfigurator) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 28c9d7958591d..61fd4ee38a329 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -34,14 +34,16 @@ class ContainerConfigurator extends AbstractConfigurator private $path; private $file; private $anonymousCount = 0; + private $defaultDefinition; - public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file) + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file, Definition $defaultDefinition = null) { $this->container = $container; $this->loader = $loader; $this->instanceof = &$instanceof; $this->path = $path; $this->file = $file; + $this->defaultDefinition = $defaultDefinition; } final public function extension(string $namespace, array $config) @@ -67,7 +69,7 @@ final public function parameters(): ParametersConfigurator final public function services(): ServicesConfigurator { - return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount); + return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount, $this->defaultDefinition); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php index f0fdde81c33a4..358303f660b80 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php @@ -32,16 +32,19 @@ class ServicesConfigurator extends AbstractConfigurator private $path; private $anonymousHash; private $anonymousCount; + private $defaultDefinition; - public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0) + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0, Definition $defaultDefinition = null) { - $this->defaults = new Definition(); + $defaultDefinition = $defaultDefinition ?? new Definition(); + $this->defaults = clone $defaultDefinition; $this->container = $container; $this->loader = $loader; $this->instanceof = &$instanceof; $this->path = $path; $this->anonymousHash = ContainerBuilder::hash($path ?: mt_rand()); $this->anonymousCount = &$anonymousCount; + $this->defaultDefinition = $defaultDefinition; $instanceof = []; } @@ -50,7 +53,7 @@ public function __construct(ContainerBuilder $container, PhpFileLoader $loader, */ final public function defaults(): DefaultsConfigurator { - return new DefaultsConfigurator($this, $this->defaults = new Definition(), $this->path); + return new DefaultsConfigurator($this, $this->defaults = clone $this->defaultDefinition, $this->path); } /** diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index a3f5012e3268f..475b5d756a3d3 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -170,11 +170,14 @@ public function process(ContainerBuilder $container) $message .= ' Did you forget to add a use statement?'; } - throw new InvalidArgumentException($message); - } + $container->register($erroredId = '.errored.'.$container->hash($message), $type) + ->addError($message); - $target = ltrim($target, '\\'); - $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior); + $args[$p->name] = new Reference($erroredId, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE); + } else { + $target = ltrim($target, '\\'); + $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior); + } } // register the maps as a per-method service-locators if ($args) { diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index a3b7969be172c..0746643036517 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -197,7 +197,7 @@ public function testSkipSetContainer() public function testExceptionOnNonExistentTypeHint() { - $this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException'); + $this->expectException('RuntimeException'); $this->expectExceptionMessage('Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClass". Did you forget to add a use statement?'); $container = new ContainerBuilder(); $container->register('argument_resolver.service')->addArgument([]); @@ -207,11 +207,17 @@ public function testExceptionOnNonExistentTypeHint() $pass = new RegisterControllerArgumentLocatorsPass(); $pass->process($container); + + $error = $container->getDefinition('argument_resolver.service')->getArgument(0); + $error = $container->getDefinition($error)->getArgument(0)['foo::fooAction']->getValues()[0]; + $error = $container->getDefinition($error)->getArgument(0)['nonExistent']->getValues()[0]; + + $container->get($error); } public function testExceptionOnNonExistentTypeHintDifferentNamespace() { - $this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException'); + $this->expectException('RuntimeException'); $this->expectExceptionMessage('Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassDifferentNamespaceController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Acme\NonExistentClass".'); $container = new ContainerBuilder(); $container->register('argument_resolver.service')->addArgument([]); @@ -221,6 +227,12 @@ public function testExceptionOnNonExistentTypeHintDifferentNamespace() $pass = new RegisterControllerArgumentLocatorsPass(); $pass->process($container); + + $error = $container->getDefinition('argument_resolver.service')->getArgument(0); + $error = $container->getDefinition($error)->getArgument(0)['foo::fooAction']->getValues()[0]; + $error = $container->getDefinition($error)->getArgument(0)['nonExistent']->getValues()[0]; + + $container->get($error); } public function testNoExceptionOnNonExistentTypeHintOptionalArg() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy