Skip to content

Commit 5e44cb4

Browse files
committed
[HttpFoundation] Fix MicroKernelTrait for php 8.
1 parent 5233cb9 commit 5e44cb4

File tree

2 files changed

+142
-26
lines changed

2 files changed

+142
-26
lines changed

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

+47-26
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
*
2828
* @author Ryan Weaver <ryan@knpuniversity.com>
2929
* @author Fabien Potencier <fabien@symfony.com>
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 = str_contains(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,20 @@ 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+
if (($configuratorClass = $this->getParameterClassName($configureContainer, 0)) && !is_a(ContainerConfigurator::class, $configuratorClass, true)) {
106117
$this->configureContainer($container, $loader);
107118

108119
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-
}
115120
}
116121

117122
// the user has opted into using the ContainerConfigurator
118123
/* @var ContainerPhpFileLoader $kernelLoader */
119-
$kernelLoader = $loader->getResolver()->resolve($file);
124+
$kernelLoader = $loader->getResolver()->resolve($file = $configureContainer->getFileName());
120125
$kernelLoader->setCurrentDir(\dirname($file));
121126
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
122127

@@ -133,12 +138,14 @@ public function registerContainerConfiguration(LoaderInterface $loader)
133138
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
134139
}
135140

136-
$container->setAlias(static::class, 'kernel')->setPublic(true);
141+
$container->setAlias($kernelClass, 'kernel')->setPublic(true);
137142
});
138143
}
139144

140145
/**
141146
* @internal
147+
*
148+
* @return RouteCollection
142149
*/
143150
public function loadRoutes(LoaderInterface $loader)
144151
{
@@ -149,28 +156,42 @@ public function loadRoutes(LoaderInterface $loader)
149156
$collection = new RouteCollection();
150157

151158
try {
152-
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
159+
$configureRoutes = new \ReflectionMethod($this, 'configureRoutes');
160+
} catch (\ReflectionException $e) {
161+
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);
162+
}
153163

154-
foreach ($collection as $route) {
155-
$controller = $route->getDefault('_controller');
164+
if (($configuratorClass = $this->getParameterClassName($configureRoutes, 0)) && !is_a(RoutingConfigurator::class, $configuratorClass, true)) {
165+
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);
156166

157-
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
158-
$route->setDefault('_controller', ['kernel', $controller[1]]);
159-
}
160-
}
167+
$routes = new RouteCollectionBuilder($loader);
168+
$this->configureRoutes($routes);
161169

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;
170+
return $routes->build();
171+
}
172+
173+
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
174+
175+
foreach ($collection as $route) {
176+
$controller = $route->getDefault('_controller');
177+
178+
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
179+
$route->setDefault('_controller', ['kernel', $controller[1]]);
166180
}
167181
}
168182

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);
183+
return $collection;
184+
}
170185

171-
$routes = new RouteCollectionBuilder($loader);
172-
$this->configureRoutes($routes);
186+
private function getParameterClassName(\ReflectionMethod $method, int $position): ?string
187+
{
188+
if ($method->getNumberOfParameters() <= $position
189+
|| !($type = $method->getParameters()[$position]->getType())
190+
|| $type->isBuiltin()
191+
) {
192+
return null;
193+
}
173194

174-
return $routes->build();
195+
return method_exists($type, 'getName') ? $type->getName() : (string) $type;
175196
}
176197
}

src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php

+95
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
16+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
1517
use Symfony\Component\DependencyInjection\ContainerBuilder;
1618
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
1719
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
20+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
1821
use Symfony\Component\HttpFoundation\Request;
22+
use Symfony\Component\HttpFoundation\Response;
23+
use Symfony\Component\HttpKernel\HttpKernelInterface;
24+
use Symfony\Component\HttpKernel\Kernel;
25+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
1926

2027
require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php';
2128

@@ -77,4 +84,92 @@ public function testSecretLoadedFromExtension()
7784

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

0 commit comments

Comments
 (0)