Skip to content

Commit fce7107

Browse files
feature #34881 [FrameworkBundle] Allow using the kernel as a registry of controllers and service factories (nicolas-grekas)
This PR was merged into the 5.1-dev branch. Discussion ---------- [FrameworkBundle] Allow using the kernel as a registry of controllers and service factories | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #28992, fix #29997 | License | MIT | Doc PR | - This PR builds on #34873 and #34872 and allows using the `Kernel` as a registry of autowired controllers and service factories. The `ContainerConfigurator` passed to `configureContainer()` defaults to declaring autowired and autoconfigured services. TL;DR: Silex is back but in a much more powerful way \o/ Here is a Kernel that just works and displays `Hello App\Foo` on the `/` route: ```php class Kernel extends BaseKernel { use MicroKernelTrait; protected function configureContainer(ContainerConfigurator $container): void { $container->services() ->load('App\\', '../src') ->set(Foo::class) ->factory([$this, 'createFoo']); } public function createFoo(Bar $bar) { return new Foo($bar); } protected function configureRoutes(RoutingConfigurator $routes): void { $routes->add('home', '/')->controller([$this, 'helloAction']); } public function helloAction(Foo $foo) { return new Response('Hello '.get_class($foo)); } } ``` Commits ------- 9c9b99cc65 [FrameworkBundle] Allow using the kernel as a registry of controllers and service factories
2 parents eaae95e + 9863a28 commit fce7107

File tree

5 files changed

+135
-7
lines changed

5 files changed

+135
-7
lines changed

Kernel/MicroKernelTrait.php

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313

1414
use Symfony\Component\Config\Loader\LoaderInterface;
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\Loader\Configurator\AbstractConfigurator;
1618
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
17-
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
19+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader as ContainerPhpFileLoader;
20+
use Symfony\Component\DependencyInjection\Reference;
1821
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
22+
use Symfony\Component\Routing\Loader\PhpFileLoader as RoutingPhpFileLoader;
1923
use Symfony\Component\Routing\RouteCollection;
2024
use Symfony\Component\Routing\RouteCollectionBuilder;
2125

@@ -93,6 +97,8 @@ public function registerContainerConfiguration(LoaderInterface $loader)
9397

9498
if (!$container->hasDefinition('kernel')) {
9599
$container->register('kernel', static::class)
100+
->addTag('controller.service_arguments')
101+
->setAutoconfigured(true)
96102
->setSynthetic(true)
97103
->setPublic(true)
98104
;
@@ -101,12 +107,9 @@ public function registerContainerConfiguration(LoaderInterface $loader)
101107
$kernelDefinition = $container->getDefinition('kernel');
102108
$kernelDefinition->addTag('routing.route_loader');
103109

104-
if ($this instanceof EventSubscriberInterface) {
105-
$kernelDefinition->addTag('kernel.event_subscriber');
106-
}
107-
108110
$container->addObjectResource($this);
109111
$container->fileExists($this->getProjectDir().'/config/bundles.php');
112+
$container->setParameter('kernel.secret', '%env(APP_SECRET)%');
110113

111114
try {
112115
$this->configureContainer($container, $loader);
@@ -120,16 +123,27 @@ public function registerContainerConfiguration(LoaderInterface $loader)
120123
}
121124
}
122125

126+
// the user has opted into using the ContainerConfigurator
127+
$defaultDefinition = (new Definition())->setAutowired(true)->setAutoconfigured(true);
128+
/* @var ContainerPhpFileLoader $kernelLoader */
123129
$kernelLoader = $loader->getResolver()->resolve($file);
124130
$kernelLoader->setCurrentDir(\dirname($file));
125131
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
126132

133+
$valuePreProcessor = AbstractConfigurator::$valuePreProcessor;
134+
AbstractConfigurator::$valuePreProcessor = function ($value) {
135+
return $this === $value ? new Reference('kernel') : $value;
136+
};
137+
127138
try {
128-
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file), $loader);
139+
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file, $defaultDefinition), $loader);
129140
} finally {
130141
$instanceof = [];
131142
$kernelLoader->registerAliasesForSinglyImplementedInterfaces();
143+
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
132144
}
145+
146+
$container->setAlias(static::class, 'kernel');
133147
});
134148
}
135149

@@ -139,13 +153,22 @@ public function registerContainerConfiguration(LoaderInterface $loader)
139153
public function loadRoutes(LoaderInterface $loader)
140154
{
141155
$file = (new \ReflectionObject($this))->getFileName();
156+
/* @var RoutingPhpFileLoader $kernelLoader */
142157
$kernelLoader = $loader->getResolver()->resolve($file);
143158
$kernelLoader->setCurrentDir(\dirname($file));
144159
$collection = new RouteCollection();
145160

146161
try {
147162
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
148163

164+
foreach ($collection as $route) {
165+
$controller = $route->getDefault('_controller');
166+
167+
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
168+
$route->setDefault('_controller', ['kernel', $controller[1]]);
169+
}
170+
}
171+
149172
return $collection;
150173
} catch (\TypeError $e) {
151174
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureRoutes() must be an instance of %s,', static::class, RouteCollectionBuilder::class))) {

Tests/Kernel/MicroKernelTraitTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
1818
use Symfony\Component\HttpFoundation\Request;
1919

20+
require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php';
21+
2022
class MicroKernelTraitTest extends TestCase
2123
{
2224
public function test()
@@ -56,4 +58,15 @@ public function testRoutingRouteLoaderTagIsAdded()
5658
$kernel->registerContainerConfiguration(new ClosureLoader($container));
5759
$this->assertTrue($container->getDefinition('kernel')->hasTag('routing.route_loader'));
5860
}
61+
62+
public function testFlexStyle()
63+
{
64+
$kernel = new FlexStyleMicroKernel('test', false);
65+
$kernel->boot();
66+
67+
$request = Request::create('/');
68+
$response = $kernel->handle($request);
69+
70+
$this->assertEquals('Have a great day!', $response->getContent());
71+
}
5972
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
4+
5+
return [
6+
FrameworkBundle::class => ['all' => true],
7+
];
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\NullLogger;
16+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
17+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
18+
use Symfony\Component\Filesystem\Filesystem;
19+
use Symfony\Component\HttpFoundation\Response;
20+
use Symfony\Component\HttpKernel\Kernel;
21+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
22+
23+
class FlexStyleMicroKernel extends Kernel
24+
{
25+
use MicroKernelTrait;
26+
27+
private $cacheDir;
28+
29+
public function halloweenAction(\stdClass $o)
30+
{
31+
return new Response($o->halloween);
32+
}
33+
34+
public function createHalloween(LoggerInterface $logger, string $halloween)
35+
{
36+
$o = new \stdClass();
37+
$o->logger = $logger;
38+
$o->halloween = $halloween;
39+
40+
return $o;
41+
}
42+
43+
public function getCacheDir(): string
44+
{
45+
return $this->cacheDir = sys_get_temp_dir().'/sf_flex_kernel';
46+
}
47+
48+
public function getLogDir(): string
49+
{
50+
return $this->cacheDir;
51+
}
52+
53+
public function __sleep(): array
54+
{
55+
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
56+
}
57+
58+
public function __wakeup()
59+
{
60+
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
61+
}
62+
63+
public function __destruct()
64+
{
65+
$fs = new Filesystem();
66+
$fs->remove($this->cacheDir);
67+
}
68+
69+
protected function configureRoutes(RoutingConfigurator $routes): void
70+
{
71+
$routes->add('halloween', '/')->controller([$this, 'halloweenAction']);
72+
}
73+
74+
protected function configureContainer(ContainerConfigurator $c)
75+
{
76+
$c->parameters()
77+
->set('halloween', 'Have a great day!');
78+
79+
$c->services()
80+
->set('logger', NullLogger::class)
81+
->set('stdClass', 'stdClass')
82+
->factory([$this, 'createHalloween'])
83+
->arg('$halloween', '%halloween%');
84+
}
85+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"ext-xml": "*",
2121
"symfony/cache": "^4.4|^5.0",
2222
"symfony/config": "^5.0",
23-
"symfony/dependency-injection": "^5.0.1",
23+
"symfony/dependency-injection": "^5.1",
2424
"symfony/error-handler": "^4.4.1|^5.0.1",
2525
"symfony/http-foundation": "^4.4|^5.0",
2626
"symfony/http-kernel": "^5.0",

0 commit comments

Comments
 (0)