Skip to content

Commit e3e3bbb

Browse files
derrabusnicolas-grekas
authored andcommitted
[FrameworkBundle] Fix MicroKernelTrait for php 8
1 parent af6f14e commit e3e3bbb

File tree

2 files changed

+137
-27
lines changed

2 files changed

+137
-27
lines changed

Kernel/MicroKernelTrait.php

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
*
2828
* @author Ryan Weaver <[email protected]>
2929
* @author Fabien Potencier <[email protected]>
30+
*
31+
* @method void configureRoutes(RoutingConfigurator $routes)
32+
* @method void configureContainer(ContainerConfigurator $c)
3033
*/
3134
trait MicroKernelTrait
3235
{
@@ -39,7 +42,7 @@ trait MicroKernelTrait
3942
* ->controller('App\Controller\AdminController::dashboard')
4043
* ;
4144
*/
42-
//abstract protected function configureRoutes(RoutingConfigurator $routes);
45+
//abstract protected function configureRoutes(RoutingConfigurator $routes): void;
4346

4447
/**
4548
* Configures the container.
@@ -58,7 +61,7 @@ trait MicroKernelTrait
5861
*
5962
* $c->parameters()->set('halloween', 'lot of fun');
6063
*/
61-
//abstract protected function configureContainer(ContainerConfigurator $c);
64+
//abstract protected function configureContainer(ContainerConfigurator $c): void;
6265

6366
/**
6467
* {@inheritdoc}
@@ -87,8 +90,10 @@ public function registerContainerConfiguration(LoaderInterface $loader)
8790
],
8891
]);
8992

93+
$kernelClass = false !== strpos(static::class, "@anonymous\0") ? parent::class : static::class;
94+
9095
if (!$container->hasDefinition('kernel')) {
91-
$container->register('kernel', static::class)
96+
$container->register('kernel', $kernelClass)
9297
->addTag('controller.service_arguments')
9398
->setAutoconfigured(true)
9499
->setSynthetic(true)
@@ -103,20 +108,22 @@ public function registerContainerConfiguration(LoaderInterface $loader)
103108
$container->fileExists($this->getProjectDir().'/config/bundles.php');
104109

105110
try {
111+
$configureContainer = new \ReflectionMethod($this, 'configureContainer');
112+
} catch (\ReflectionException $e) {
113+
throw new \LogicException(sprintf('"%s" uses "%s", but does not implement the required method "protected function configureContainer(ContainerConfigurator $c): void".', get_debug_type($this), MicroKernelTrait::class), 0, $e);
114+
}
115+
116+
$configuratorClass = $configureContainer->getNumberOfParameters() > 0 && ($type = $configureContainer->getParameters()[0]->getType()) && !$type->isBuiltin() ? $type->getName() : null;
117+
118+
if ($configuratorClass && !is_a(ContainerConfigurator::class, $configuratorClass, true)) {
106119
$this->configureContainer($container, $loader);
107120

108121
return;
109-
} catch (\TypeError $e) {
110-
$file = $e->getFile();
111-
112-
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureContainer() must be an instance of %s,', static::class, ContainerConfigurator::class))) {
113-
throw $e;
114-
}
115122
}
116123

117124
// the user has opted into using the ContainerConfigurator
118125
/* @var ContainerPhpFileLoader $kernelLoader */
119-
$kernelLoader = $loader->getResolver()->resolve($file);
126+
$kernelLoader = $loader->getResolver()->resolve($file = $configureContainer->getFileName());
120127
$kernelLoader->setCurrentDir(\dirname($file));
121128
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
122129

@@ -133,12 +140,14 @@ public function registerContainerConfiguration(LoaderInterface $loader)
133140
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
134141
}
135142

136-
$container->setAlias(static::class, 'kernel')->setPublic(true);
143+
$container->setAlias($kernelClass, 'kernel')->setPublic(true);
137144
});
138145
}
139146

140147
/**
141148
* @internal
149+
*
150+
* @return RouteCollection
142151
*/
143152
public function loadRoutes(LoaderInterface $loader)
144153
{
@@ -149,28 +158,32 @@ public function loadRoutes(LoaderInterface $loader)
149158
$collection = new RouteCollection();
150159

151160
try {
152-
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
161+
$configureRoutes = new \ReflectionMethod($this, 'configureRoutes');
162+
} catch (\ReflectionException $e) {
163+
throw new \LogicException(sprintf('"%s" uses "%s", but does not implement the required method "protected function configureRoutes(RoutingConfigurator $routes): void".', get_debug_type($this), MicroKernelTrait::class), 0, $e);
164+
}
153165

154-
foreach ($collection as $route) {
155-
$controller = $route->getDefault('_controller');
166+
$configuratorClass = $configureRoutes->getNumberOfParameters() > 0 && ($type = $configureRoutes->getParameters()[0]->getType()) && !$type->isBuiltin() ? $type->getName() : null;
156167

157-
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
158-
$route->setDefault('_controller', ['kernel', $controller[1]]);
159-
}
160-
}
168+
if ($configuratorClass && !is_a(RoutingConfigurator::class, $configuratorClass, true)) {
169+
trigger_deprecation('symfony/framework-bundle', '5.1', 'Using type "%s" for argument 1 of method "%s:configureRoutes()" is deprecated, use "%s" instead.', RouteCollectionBuilder::class, self::class, RoutingConfigurator::class);
161170

162-
return $collection;
163-
} catch (\TypeError $e) {
164-
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureRoutes() must be an instance of %s,', static::class, RouteCollectionBuilder::class))) {
165-
throw $e;
166-
}
171+
$routes = new RouteCollectionBuilder($loader);
172+
$this->configureRoutes($routes);
173+
174+
return $routes->build();
167175
}
168176

169-
trigger_deprecation('symfony/framework-bundle', '5.1', 'Using type "%s" for argument 1 of method "%s:configureRoutes()" is deprecated, use "%s" instead.', RouteCollectionBuilder::class, self::class, RoutingConfigurator::class);
177+
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
170178

171-
$routes = new RouteCollectionBuilder($loader);
172-
$this->configureRoutes($routes);
179+
foreach ($collection as $route) {
180+
$controller = $route->getDefault('_controller');
181+
182+
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
183+
$route->setDefault('_controller', ['kernel', $controller[1]]);
184+
}
185+
}
173186

174-
return $routes->build();
187+
return $collection;
175188
}
176189
}

Tests/Kernel/MicroKernelTraitTest.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,18 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Psr\Log\NullLogger;
16+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
17+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
1518
use Symfony\Component\DependencyInjection\ContainerBuilder;
1619
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
1720
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
21+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
1822
use Symfony\Component\HttpFoundation\Request;
23+
use Symfony\Component\HttpFoundation\Response;
24+
use Symfony\Component\HttpKernel\HttpKernelInterface;
25+
use Symfony\Component\HttpKernel\Kernel;
26+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1927

2028
require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php';
2129

@@ -77,4 +85,93 @@ public function testSecretLoadedFromExtension()
7785

7886
self::assertSame('$ecret', $kernel->getContainer()->getParameter('kernel.secret'));
7987
}
88+
89+
public function testAnonymousMicroKernel()
90+
{
91+
$kernel = new class('anonymous_kernel') extends MinimalKernel {
92+
public function helloAction(): Response
93+
{
94+
return new Response('Hello World!');
95+
}
96+
97+
protected function configureContainer(ContainerConfigurator $c): void
98+
{
99+
$c->extension('framework', [
100+
'router' => ['utf8' => true],
101+
]);
102+
$c->services()->set('logger', NullLogger::class);
103+
}
104+
105+
protected function configureRoutes(RoutingConfigurator $routes): void
106+
{
107+
$routes->add('hello', '/')->controller([$this, 'helloAction']);
108+
}
109+
};
110+
111+
$request = Request::create('/');
112+
$response = $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
113+
114+
$this->assertSame('Hello World!', $response->getContent());
115+
}
116+
117+
public function testMissingConfigureContainer()
118+
{
119+
$kernel = new class('missing_configure_container') extends MinimalKernel {
120+
protected function configureRoutes(RoutingConfigurator $routes): void
121+
{
122+
}
123+
};
124+
125+
$this->expectException(\LogicException::class);
126+
$this->expectExceptionMessage('"Symfony\Bundle\FrameworkBundle\Tests\Kernel\MinimalKernel@anonymous" uses "Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait", but does not implement the required method "protected function configureContainer(ContainerConfigurator $c): void".');
127+
128+
$kernel->boot();
129+
}
130+
131+
public function testMissingConfigureRoutes()
132+
{
133+
$kernel = new class('missing_configure_routes') extends MinimalKernel {
134+
protected function configureContainer(ContainerConfigurator $c): void
135+
{
136+
$c->extension('framework', [
137+
'router' => ['utf8' => true],
138+
]);
139+
}
140+
};
141+
142+
$this->expectException(\LogicException::class);
143+
$this->expectExceptionMessage('"Symfony\Bundle\FrameworkBundle\Tests\Kernel\MinimalKernel@anonymous" uses "Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait", but does not implement the required method "protected function configureRoutes(RoutingConfigurator $routes): void".');
144+
145+
$request = Request::create('/');
146+
$kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
147+
}
148+
}
149+
150+
abstract class MinimalKernel extends Kernel
151+
{
152+
use MicroKernelTrait;
153+
154+
private $cacheDir;
155+
156+
public function __construct(string $cacheDir)
157+
{
158+
parent::__construct('test', false);
159+
160+
$this->cacheDir = sys_get_temp_dir().'/'.$cacheDir;
161+
}
162+
163+
public function registerBundles(): iterable
164+
{
165+
yield new FrameworkBundle();
166+
}
167+
168+
public function getCacheDir(): string
169+
{
170+
return $this->cacheDir;
171+
}
172+
173+
public function getLogDir(): string
174+
{
175+
return $this->cacheDir;
176+
}
80177
}

0 commit comments

Comments
 (0)