Skip to content

Commit 11bc128

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

File tree

9 files changed

+157
-17
lines changed

9 files changed

+157
-17
lines changed

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +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;
17-
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
19+
use Symfony\Component\DependencyInjection\Reference;
1820
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1921
use Symfony\Component\Routing\RouteCollection;
2022
use Symfony\Component\Routing\RouteCollectionBuilder;
@@ -93,6 +95,8 @@ public function registerContainerConfiguration(LoaderInterface $loader)
9395

9496
if (!$container->hasDefinition('kernel')) {
9597
$container->register('kernel', static::class)
98+
->addTag('controller.service_arguments')
99+
->setAutoconfigured(true)
96100
->setSynthetic(true)
97101
->setPublic(true)
98102
;
@@ -101,14 +105,11 @@ public function registerContainerConfiguration(LoaderInterface $loader)
101105
$kernelDefinition = $container->getDefinition('kernel');
102106
$kernelDefinition->addTag('routing.route_loader');
103107

104-
if ($this instanceof EventSubscriberInterface) {
105-
$kernelDefinition->addTag('kernel.event_subscriber');
106-
}
107-
108108
$container->addObjectResource($this);
109109
$container->fileExists($this->getProjectDir().'/config/bundles.php');
110110
$container->setParameter('container.dumper.inline_class_loader', \PHP_VERSION_ID < 70400 || !ini_get('opcache.preload'));
111111
$container->setParameter('container.dumper.inline_factories', true);
112+
$container->setParameter('kernel.secret', '%env(APP_SECRET)%');
112113

113114
try {
114115
$this->configureContainer($container, $loader);
@@ -122,16 +123,25 @@ public function registerContainerConfiguration(LoaderInterface $loader)
122123
}
123124
}
124125

126+
$defaultDefinition = (new Definition())->setAutowired(true)->setAutoconfigured(true);
125127
$kernelLoader = $loader->getResolver()->resolve($file);
126128
$kernelLoader->setCurrentDir(\dirname($file));
127129
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
128130

131+
$valuePreProcessor = AbstractConfigurator::$valuePreProcessor;
132+
AbstractConfigurator::$valuePreProcessor = function ($value) {
133+
return $this === $value ? new Reference('kernel') : $value;
134+
};
135+
129136
try {
130-
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file), $loader);
137+
$this->configureContainer(new ContainerConfigurator($container, $kernelLoader, $instanceof, $file, $file, $defaultDefinition), $loader);
131138
} finally {
132139
$instanceof = [];
133140
$kernelLoader->registerAliasesForSinglyImplementedInterfaces();
141+
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
134142
}
143+
144+
$container->setAlias(static::class, 'kernel');
135145
});
136146
}
137147

@@ -148,6 +158,14 @@ public function loadRoutes(LoaderInterface $loader)
148158
try {
149159
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
150160

161+
foreach ($collection as $route) {
162+
$controller = $route->getDefault('_controller');
163+
164+
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
165+
$route->setDefault('_controller', ['kernel', $controller[1]]);
166+
}
167+
}
168+
151169
return $collection;
152170
} catch (\TypeError $e) {
153171
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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,14 @@ public function process(ContainerBuilder $container)
170170
$message .= ' Did you forget to add a use statement?';
171171
}
172172

173-
throw new InvalidArgumentException($message);
174-
}
173+
$container->register($erroredId = '.errored.'.$container->hash($message), $type)
174+
->addError($message);
175175

176-
$target = ltrim($target, '\\');
177-
$args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior);
176+
$args[$p->name] = new Reference($erroredId, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE);
177+
} else {
178+
$target = ltrim($target, '\\');
179+
$args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, $p->name) : new Reference($target, $invalidBehavior);
180+
}
178181
}
179182
// register the maps as a per-method service-locators
180183
if ($args) {

0 commit comments

Comments
 (0)