Issue #3412168 by godotislate, catch, smustgrave, longwave: Add back "final" to Drupal\big_pipe\StackMiddleware\ContentLength

merge-requests/4611/merge
Dave Long 2024-01-12 17:54:43 +00:00
parent af726dbfa5
commit c10494df94
No known key found for this signature in database
GPG Key ID: ED52AE211E142771
6 changed files with 121 additions and 1 deletions

View File

@ -6,6 +6,8 @@ use Drupal\Core\StackMiddleware\StackedHttpKernel;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
/**
* Provides a compiler pass for stacked HTTP kernels.
@ -100,7 +102,14 @@ class StackedKernelPass implements CompilerPassInterface {
$first_responder = FALSE;
}
elseif ($first_responder) {
// Use interface proxying to allow middleware classes declared final
// to be set as lazy.
$decorator->setLazy(TRUE);
foreach ([HttpKernelInterface::class, TerminableInterface::class] as $interface) {
if (is_a($decorator->getClass(), $interface, TRUE)) {
$decorator->addTag('proxy', ['interface' => $interface]);
}
}
}
$decorated_id = $id;

View File

@ -53,6 +53,9 @@ class StackedHttpKernel implements HttpKernelInterface, TerminableInterface {
/**
* {@inheritdoc}
*
* phpcs:ignore Drupal.Commenting.FunctionComment.VoidReturn
* @return void
*/
public function terminate(Request $request, Response $response) {
$previous = NULL;

View File

@ -12,7 +12,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Defines a big pipe middleware that removes Content-Length headers.
*/
class ContentLength implements HttpKernelInterface {
final class ContentLength implements HttpKernelInterface {
/**
* Constructs a new ContentLength instance.

View File

@ -7,8 +7,13 @@ namespace Drupal\Tests\Core\DependencyInjection\Compiler;
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\StackMiddleware\StackedHttpKernel;
use Drupal\Tests\Core\DependencyInjection\Fixture\FinalTestHttpMiddlewareClass;
use Drupal\Tests\Core\DependencyInjection\Fixture\FinalTestNonTerminableHttpMiddlewareClass;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* @coversDefaultClass \Drupal\Core\DependencyInjection\Compiler\StackedKernelPass
@ -95,6 +100,58 @@ class StackedKernelPassTest extends UnitTestCase {
$this->assertSame($kernel->getArguments(), $unprocessed_kernel->getArguments());
}
/**
* Tests that class declared 'final' can be added as http_middleware.
*/
public function testProcessWithStackedKernelAndFinalHttpMiddleware(): void {
$stacked_kernel = new Definition(StackedHttpKernel::class);
$stacked_kernel->setPublic(TRUE);
$this->containerBuilder->setDefinition('http_kernel', $stacked_kernel);
$basic_kernel = $this->getMockBuilder(HttpKernel::class)
->disableOriginalConstructor()
->onlyMethods(['handle', 'terminate'])
->getMock();
$basic_definition = (new Definition($basic_kernel::class))
->setPublic(TRUE);
$this->containerBuilder->setDefinition('http_kernel.basic', $basic_definition);
// Services tagged 'http_middleware', other than the highest priority
// middleware that is a responder, is also set as lazy by
// StackedKernelPass::process(). Add middleware classes declared final and
// confirm they are interface proxied correctly.
// @see https://symfony.com/doc/current/service_container/lazy_services.html#interface-proxifying
$first_responder = $this->getMockBuilder(HttpKernelInterface::class)
->getMock();
$this->containerBuilder->setDefinition('http_kernel.one', (new Definition($first_responder::class))
->setPublic(TRUE)
->addTag('http_middleware', [
'priority' => 200,
'responder' => TRUE,
]));
// First middleware class declared final.
$this->containerBuilder->setDefinition('http_kernel.two', (new Definition(FinalTestHttpMiddlewareClass::class))
->setPublic(TRUE)
->addTag('http_middleware', [
'priority' => 100,
'responder' => TRUE,
]));
// Second middleware class declared final, this time without implementing
// TerminableInterface.
$this->containerBuilder->setDefinition('http_kernel.three', (new Definition(FinalTestNonTerminableHttpMiddlewareClass::class))
->setPublic(TRUE)
->addTag('http_middleware', [
'priority' => 50,
'responder' => TRUE,
]));
$this->stackedKernelPass->process($this->containerBuilder);
try {
$this->containerBuilder->get('http_kernel');
}
catch (InvalidArgumentException $e) {
$this->fail($e->getMessage());
}
}
/**
* Creates a middleware definition.
*

View File

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\Core\DependencyInjection\Fixture;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
/**
* Stub of http_middleware class that is declared final.
*/
final class FinalTestHttpMiddlewareClass implements HttpKernelInterface, TerminableInterface {
/**
* {@inheritdoc}
*/
public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = TRUE): Response {
return new Response();
}
/**
* {@inheritdoc}
*
* phpcs:ignore Drupal.Commenting.FunctionComment.VoidReturn
* @return void
*/
public function terminate(Request $request, Response $response) {}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Drupal\Tests\Core\DependencyInjection\Fixture;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Stub of http_middleware class that is declared final but is not terminable.
*/
final class FinalTestNonTerminableHttpMiddlewareClass implements HttpKernelInterface {
/**
* {@inheritdoc}
*/
public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = TRUE): Response {
return new Response();
}
}