Skip to content

Commit cc9f9f2

Browse files
[FrameworkBundle] Allow using the kernel as a registry of controllers and service factories
1 parent fb2c385 commit cc9f9f2

File tree

9 files changed

+164
-8
lines changed

9 files changed

+164
-8
lines changed

src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
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;
19+
use Symfony\Component\DependencyInjection\Reference;
1720
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1821
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1922
use Symfony\Component\Routing\RouteCollection;
@@ -93,6 +96,7 @@ public function registerContainerConfiguration(LoaderInterface $loader)
9396

9497
if (!$container->hasDefinition('kernel')) {
9598
$container->register('kernel', static::class)
99+
->addTag('controller.service_arguments')
96100
->setSynthetic(true)
97101
->setPublic(true)
98102
;
@@ -109,6 +113,7 @@ public function registerContainerConfiguration(LoaderInterface $loader)
109113
$container->fileExists($this->getProjectDir().'/config/bundles.php');
110114
$container->setParameter('container.dumper.inline_class_loader', \PHP_VERSION_ID < 70400 || !ini_get('opcache.preload'));
111115
$container->setParameter('container.dumper.inline_factories', true);
116+
$container->setParameter('kernel.secret', '%env(APP_SECRET)%');
112117

113118
try {
114119
$this->configureContainer($container, $loader);
@@ -122,16 +127,33 @@ public function registerContainerConfiguration(LoaderInterface $loader)
122127
}
123128
}
124129

130+
$defaultDefinition = (new Definition())->setAutowired(true)->setAutoconfigured(true);
125131
$kernelLoader = $loader->getResolver()->resolve($file);
126132
$kernelLoader->setCurrentDir(\dirname($file));
127133
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
128134

135+
$valuePreProcessor = AbstractConfigurator::$valuePreProcessor;
136+
AbstractConfigurator::$valuePreProcessor = function ($value) use ($kernelDefinition) {
137+
if ($this === $value) {
138+
return new Reference('kernel');
139+
}
140+
141+
if (\is_array($value) && [0, 1] === array_keys($value) && $value[0] instanceof Reference && 'kernel' === (string) $value[0] && method_exists($this, $value[1])) {
142+
$kernelDefinition->addTag('controller.service_arguments', ['exclude_method' => $value[1]]);
143+
}
144+
145+
return $value;
146+
};
147+
129148
try {
130-
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file), $loader);
149+
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file, $defaultDefinition), $loader);
131150
} finally {
132151
$instanceof = [];
133152
$kernelLoader->registerAliasesForSinglyImplementedInterfaces();
153+
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
134154
}
155+
156+
$container->setAlias(static::class, 'kernel');
135157
});
136158
}
137159

@@ -148,6 +170,14 @@ public function loadRoutes(LoaderInterface $loader)
148170
try {
149171
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
150172

173+
foreach ($collection as $route) {
174+
$controller = $route->getDefault('_controller');
175+
176+
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
177+
$route->setDefault('_controller', ['kernel', $controller[1]]);
178+
}
179+
}
180+
151181
return $collection;
152182
} catch (\TypeError $e) {
153183
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureRoutes() must be an instance of %s,', static::class, RouteCollectionBuilder::class))) {

src/Symfony/Bundle/FrameworkBundle/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 <fabien@symfony.com>
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+
}

src/Symfony/Bundle/FrameworkBundle/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",

src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ abstract class AbstractConfigurator
2222
{
2323
const FACTORY = 'unknown';
2424

25+
/**
26+
* @var callable(mixed $value, bool $allowService)|null
27+
*/
28+
public static $valuePreProcessor;
29+
2530
/** @internal */
2631
protected $definition;
2732

@@ -49,7 +54,11 @@ public static function processValue($value, $allowServices = false)
4954
$value[$k] = static::processValue($v, $allowServices);
5055
}
5156

52-
return $value;
57+
return self::$valuePreProcessor ? (self::$valuePreProcessor)($value, $allowServices) : $value;
58+
}
59+
60+
if (self::$valuePreProcessor) {
61+
$value = (self::$valuePreProcessor)($value, $allowServices);
5362
}
5463

5564
if ($value instanceof ReferenceConfigurator) {

src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ class ContainerConfigurator extends AbstractConfigurator
3434
private $path;
3535
private $file;
3636
private $anonymousCount = 0;
37+
private $defaultDefinition;
3738

38-
public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file)
39+
public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file, Definition $defaultDefinition = null)
3940
{
4041
$this->container = $container;
4142
$this->loader = $loader;
4243
$this->instanceof = &$instanceof;
4344
$this->path = $path;
4445
$this->file = $file;
46+
$this->defaultDefinition = $defaultDefinition;
4547
}
4648

4749
final public function extension(string $namespace, array $config)
@@ -73,7 +75,7 @@ final public function parameters(): ParametersConfigurator
7375

7476
final public function services(): ServicesConfigurator
7577
{
76-
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount);
78+
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount, $this->defaultDefinition);
7779
}
7880
}
7981

src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,19 @@ class ServicesConfigurator extends AbstractConfigurator
3232
private $path;
3333
private $anonymousHash;
3434
private $anonymousCount;
35+
private $defaultDefinition;
3536

36-
public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0)
37+
public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0, Definition $defaultDefinition = null)
3738
{
38-
$this->defaults = new Definition();
39+
$defaultDefinition = $defaultDefinition ?? new Definition();
40+
$this->defaults = clone $defaultDefinition;
3941
$this->container = $container;
4042
$this->loader = $loader;
4143
$this->instanceof = &$instanceof;
4244
$this->path = $path;
4345
$this->anonymousHash = ContainerBuilder::hash($path ?: mt_rand());
4446
$this->anonymousCount = &$anonymousCount;
47+
$this->defaultDefinition = $defaultDefinition;
4548
$instanceof = [];
4649
}
4750

@@ -50,7 +53,7 @@ public function __construct(ContainerBuilder $container, PhpFileLoader $loader,
5053
*/
5154
final public function defaults(): DefaultsConfigurator
5255
{
53-
return new DefaultsConfigurator($this, $this->defaults = new Definition(), $this->path);
56+
return new DefaultsConfigurator($this, $this->defaults = clone $this->defaultDefinition, $this->path);
5457
}
5558

5659
/**

src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ public function process(ContainerBuilder $container)
8585
}
8686
}
8787

88+
foreach ($tags as $i => $attributes) {
89+
if (isset($attributes['exclude_method'])) {
90+
unset($methods[strtolower($attributes['exclude_method'])]);
91+
unset($tags[$i]);
92+
}
93+
}
94+
8895
// validate and collect explicit per-actions and per-arguments service references
8996
foreach ($tags as $attributes) {
9097
if (!isset($attributes['action']) && !isset($attributes['argument']) && !isset($attributes['id'])) {

0 commit comments

Comments
 (0)