[FrameworkBundle] Fix MicroKernelTrait for php 8

This commit is contained in:
Alexander M. Turek 2020-05-24 16:04:59 +02:00 committed by Nicolas Grekas
parent bf43fa3b34
commit 7f3132ebe3
3 changed files with 138 additions and 30 deletions

View File

@ -31,8 +31,6 @@ matrix:
- php: nightly - php: nightly
services: [memcached] services: [memcached]
fast_finish: true fast_finish: true
allow_failures:
- php: nightly
cache: cache:
directories: directories:

View File

@ -27,6 +27,9 @@ use Symfony\Component\Routing\RouteCollectionBuilder;
* *
* @author Ryan Weaver <ryan@knpuniversity.com> * @author Ryan Weaver <ryan@knpuniversity.com>
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*
* @method void configureRoutes(RoutingConfigurator $routes)
* @method void configureContainer(ContainerConfigurator $c)
*/ */
trait MicroKernelTrait trait MicroKernelTrait
{ {
@ -39,7 +42,7 @@ trait MicroKernelTrait
* ->controller('App\Controller\AdminController::dashboard') * ->controller('App\Controller\AdminController::dashboard')
* ; * ;
*/ */
//abstract protected function configureRoutes(RoutingConfigurator $routes); //abstract protected function configureRoutes(RoutingConfigurator $routes): void;
/** /**
* Configures the container. * Configures the container.
@ -58,7 +61,7 @@ trait MicroKernelTrait
* *
* $c->parameters()->set('halloween', 'lot of fun'); * $c->parameters()->set('halloween', 'lot of fun');
*/ */
//abstract protected function configureContainer(ContainerConfigurator $c); //abstract protected function configureContainer(ContainerConfigurator $c): void;
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -87,8 +90,10 @@ trait MicroKernelTrait
], ],
]); ]);
$kernelClass = false !== strpos(static::class, "@anonymous\0") ? parent::class : static::class;
if (!$container->hasDefinition('kernel')) { if (!$container->hasDefinition('kernel')) {
$container->register('kernel', static::class) $container->register('kernel', $kernelClass)
->addTag('controller.service_arguments') ->addTag('controller.service_arguments')
->setAutoconfigured(true) ->setAutoconfigured(true)
->setSynthetic(true) ->setSynthetic(true)
@ -103,20 +108,22 @@ trait MicroKernelTrait
$container->fileExists($this->getProjectDir().'/config/bundles.php'); $container->fileExists($this->getProjectDir().'/config/bundles.php');
try { try {
$configureContainer = new \ReflectionMethod($this, 'configureContainer');
} catch (\ReflectionException $e) {
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);
}
$configuratorClass = $configureContainer->getNumberOfParameters() > 0 && ($type = $configureContainer->getParameters()[0]->getType()) && !$type->isBuiltin() ? $type->getName() : null;
if ($configuratorClass && !is_a(ContainerConfigurator::class, $configuratorClass, true)) {
$this->configureContainer($container, $loader); $this->configureContainer($container, $loader);
return; return;
} catch (\TypeError $e) {
$file = $e->getFile();
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureContainer() must be an instance of %s,', static::class, ContainerConfigurator::class))) {
throw $e;
}
} }
// the user has opted into using the ContainerConfigurator // the user has opted into using the ContainerConfigurator
/* @var ContainerPhpFileLoader $kernelLoader */ /* @var ContainerPhpFileLoader $kernelLoader */
$kernelLoader = $loader->getResolver()->resolve($file); $kernelLoader = $loader->getResolver()->resolve($file = $configureContainer->getFileName());
$kernelLoader->setCurrentDir(\dirname($file)); $kernelLoader->setCurrentDir(\dirname($file));
$instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)(); $instanceof = &\Closure::bind(function &() { return $this->instanceof; }, $kernelLoader, $kernelLoader)();
@ -133,12 +140,14 @@ trait MicroKernelTrait
AbstractConfigurator::$valuePreProcessor = $valuePreProcessor; AbstractConfigurator::$valuePreProcessor = $valuePreProcessor;
} }
$container->setAlias(static::class, 'kernel')->setPublic(true); $container->setAlias($kernelClass, 'kernel')->setPublic(true);
}); });
} }
/** /**
* @internal * @internal
*
* @return RouteCollection
*/ */
public function loadRoutes(LoaderInterface $loader) public function loadRoutes(LoaderInterface $loader)
{ {
@ -149,28 +158,32 @@ trait MicroKernelTrait
$collection = new RouteCollection(); $collection = new RouteCollection();
try { try {
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file)); $configureRoutes = new \ReflectionMethod($this, 'configureRoutes');
} catch (\ReflectionException $e) {
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);
}
foreach ($collection as $route) { $configuratorClass = $configureRoutes->getNumberOfParameters() > 0 && ($type = $configureRoutes->getParameters()[0]->getType()) && !$type->isBuiltin() ? $type->getName() : null;
$controller = $route->getDefault('_controller');
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) { if ($configuratorClass && !is_a(RoutingConfigurator::class, $configuratorClass, true)) {
$route->setDefault('_controller', ['kernel', $controller[1]]); 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);
}
}
return $collection; $routes = new RouteCollectionBuilder($loader);
} catch (\TypeError $e) { $this->configureRoutes($routes);
if (0 !== strpos($e->getMessage(), sprintf('Argument 1 passed to %s::configureRoutes() must be an instance of %s,', static::class, RouteCollectionBuilder::class))) {
throw $e; return $routes->build();
}
$this->configureRoutes(new RoutingConfigurator($collection, $kernelLoader, $file, $file));
foreach ($collection as $route) {
$controller = $route->getDefault('_controller');
if (\is_array($controller) && [0, 1] === array_keys($controller) && $this === $controller[0]) {
$route->setDefault('_controller', ['kernel', $controller[1]]);
} }
} }
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); return $collection;
$routes = new RouteCollectionBuilder($loader);
$this->configureRoutes($routes);
return $routes->build();
} }
} }

View File

@ -12,10 +12,18 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php'; require_once __DIR__.'/flex-style/src/FlexStyleMicroKernel.php';
@ -77,4 +85,93 @@ class MicroKernelTraitTest extends TestCase
self::assertSame('$ecret', $kernel->getContainer()->getParameter('kernel.secret')); self::assertSame('$ecret', $kernel->getContainer()->getParameter('kernel.secret'));
} }
public function testAnonymousMicroKernel()
{
$kernel = new class('anonymous_kernel') extends MinimalKernel {
public function helloAction(): Response
{
return new Response('Hello World!');
}
protected function configureContainer(ContainerConfigurator $c): void
{
$c->extension('framework', [
'router' => ['utf8' => true],
]);
$c->services()->set('logger', NullLogger::class);
}
protected function configureRoutes(RoutingConfigurator $routes): void
{
$routes->add('hello', '/')->controller([$this, 'helloAction']);
}
};
$request = Request::create('/');
$response = $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
$this->assertSame('Hello World!', $response->getContent());
}
public function testMissingConfigureContainer()
{
$kernel = new class('missing_configure_container') extends MinimalKernel {
protected function configureRoutes(RoutingConfigurator $routes): void
{
}
};
$this->expectException(\LogicException::class);
$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".');
$kernel->boot();
}
public function testMissingConfigureRoutes()
{
$kernel = new class('missing_configure_routes') extends MinimalKernel {
protected function configureContainer(ContainerConfigurator $c): void
{
$c->extension('framework', [
'router' => ['utf8' => true],
]);
}
};
$this->expectException(\LogicException::class);
$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".');
$request = Request::create('/');
$kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, false);
}
}
abstract class MinimalKernel extends Kernel
{
use MicroKernelTrait;
private $cacheDir;
public function __construct(string $cacheDir)
{
parent::__construct('test', false);
$this->cacheDir = sys_get_temp_dir().'/'.$cacheDir;
}
public function registerBundles(): iterable
{
yield new FrameworkBundle();
}
public function getCacheDir(): string
{
return $this->cacheDir;
}
public function getLogDir(): string
{
return $this->cacheDir;
}
} }