Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c9b99c

Browse files
[FrameworkBundle] Allow using the kernel as a registry of controllers and service factories
1 parent 8c80c5b commit 9c9b99c

File tree

10 files changed

+176
-19
lines changed

10 files changed

+176
-19
lines changed

src/Symfony/Bundle/FrameworkBundle/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))) {

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 <[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+
}

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)
@@ -67,7 +69,7 @@ final public function parameters(): ParametersConfigurator
6769

6870
final public function services(): ServicesConfigurator
6971
{
70-
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount);
72+
return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount, $this->defaultDefinition);
7173
}
7274
}
7375

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) {

src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public function testSkipSetContainer()
197197

198198
public function testExceptionOnNonExistentTypeHint()
199199
{
200-
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
200+
$this->expectException('RuntimeException');
201201
$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?');
202202
$container = new ContainerBuilder();
203203
$container->register('argument_resolver.service')->addArgument([]);
@@ -207,11 +207,17 @@ public function testExceptionOnNonExistentTypeHint()
207207

208208
$pass = new RegisterControllerArgumentLocatorsPass();
209209
$pass->process($container);
210+
211+
$error = $container->getDefinition('argument_resolver.service')->getArgument(0);
212+
$error = $container->getDefinition($error)->getArgument(0)['foo::fooAction']->getValues()[0];
213+
$error = $container->getDefinition($error)->getArgument(0)['nonExistent']->getValues()[0];
214+
215+
$container->get($error);
210216
}
211217

212218
public function testExceptionOnNonExistentTypeHintDifferentNamespace()
213219
{
214-
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
220+
$this->expectException('RuntimeException');
215221
$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".');
216222
$container = new ContainerBuilder();
217223
$container->register('argument_resolver.service')->addArgument([]);
@@ -221,6 +227,12 @@ public function testExceptionOnNonExistentTypeHintDifferentNamespace()
221227

222228
$pass = new RegisterControllerArgumentLocatorsPass();
223229
$pass->process($container);
230+
231+
$error = $container->getDefinition('argument_resolver.service')->getArgument(0);
232+
$error = $container->getDefinition($error)->getArgument(0)['foo::fooAction']->getValues()[0];
233+
$error = $container->getDefinition($error)->getArgument(0)['nonExistent']->getValues()[0];
234+
235+
$container->get($error);
224236
}
225237

226238
public function testNoExceptionOnNonExistentTypeHintOptionalArg()

0 commit comments

Comments
 (0)