diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php index d9c16b36c5eea..2a2c1983166b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php @@ -60,8 +60,6 @@ abstract class AbstractController implements ServiceSubscriberInterface */ protected $container; - private ?HttpHeaderSerializer $httpHeaderSerializer = null; - #[Required] public function setContainer(ContainerInterface $container): ?ContainerInterface { @@ -96,6 +94,7 @@ public static function getSubscribedServices(): array 'security.token_storage' => '?'.TokenStorageInterface::class, 'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class, 'parameter_bag' => '?'.ContainerBagInterface::class, + 'web_link.http_header_serializer' => '?'.HttpHeaderSerializer::class, ]; } @@ -412,32 +411,24 @@ protected function addLink(Request $request, LinkInterface $link): void */ protected function sendEarlyHints(iterable $links, Response $response = null): Response { - if (!class_exists(HttpHeaderSerializer::class)) { + if (!$this->container->has('web_link.http_header_serializer')) { throw new \LogicException('You cannot use the "sendEarlyHints" method if the WebLink component is not available. Try running "composer require symfony/web-link".'); } - if (null === $response) { - $response = new Response(); - } - - if (null === $this->httpHeaderSerializer) { - $this->httpHeaderSerializer = new HttpHeaderSerializer(); - } - - $response->headers->set('Link', $this->httpHeaderSerializer->serialize($this->populateEarlyHints($links)), false); - $response->sendHeaders(103); - - return $response; - } + $response ??= new Response(); - private function populateEarlyHints(iterable $links): \Generator - { + $populatedLinks = []; foreach ($links as $link) { if ($link instanceof EvolvableLinkInterface && !$link->getRels()) { $link = $link->withRel('preload'); } - yield $link; + $populatedLinks[] = $link; } + + $response->headers->set('Link', $this->container->get('web_link.http_header_serializer')->serialize($populatedLinks), false); + $response->sendHeaders(103); + + return $response; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php index 0b0e79db8c1bf..64345cc997717 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php @@ -12,10 +12,18 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener; +use Symfony\Component\WebLink\HttpHeaderSerializer; return static function (ContainerConfigurator $container) { $container->services() + + ->set('web_link.http_header_serializer', HttpHeaderSerializer::class) + ->alias(HttpHeaderSerializer::class, 'web_link.http_header_serializer') + ->set('web_link.add_link_header_listener', AddLinkHeaderListener::class) + ->args([ + service('web_link.http_header_serializer'), + ]) ->tag('kernel.event_subscriber') ; }; diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 13e3c40ad5287..9b2c0bcf4e4cf 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -113,7 +113,7 @@ CHANGELOG make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to update your database. * `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column, - make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database + make sure to run `CREATE INDEX expiry ON sessions (sess_lifetime)` to update your database to speed up garbage collection of expired sessions. * added `SessionHandlerFactory` to create session handlers with a DSN * added `IpUtils::anonymize()` to help with GDPR compliance. diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index c609ee9fca377..888c6ad858aaa 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -331,11 +331,11 @@ public function prepare(Request $request): static /** * Sends HTTP headers. * - * @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null. + * @param null|positive-int $statusCode The status code to use, override the statusCode property if set and not null * * @return $this */ - public function sendHeaders(/* ?int $statusCode = null */): static + public function sendHeaders(/* int $statusCode = null */): static { // headers have already been sent by the developer if (headers_sent()) { diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index 6c3eb1a2fc1ea..2c8ff15f3650e 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -59,11 +59,11 @@ public function setCallback(callable $callback): static /** * This method only sends the headers once. * - * @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null. + * @param null|positive-int $statusCode The status code to use, override the statusCode property if set and not null * * @return $this */ - public function sendHeaders(/* ?int $statusCode = null */): static + public function sendHeaders(/* int $statusCode = null */): static { if ($this->headersSent) { return $this; diff --git a/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php b/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php index e769591ead767..46fcf3ef830de 100644 --- a/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php +++ b/src/Symfony/Component/WebLink/EventListener/AddLinkHeaderListener.php @@ -29,11 +29,10 @@ class_exists(HttpHeaderSerializer::class); */ class AddLinkHeaderListener implements EventSubscriberInterface { - private HttpHeaderSerializer $serializer; - - public function __construct() + public function __construct( + private readonly HttpHeaderSerializer $serializer = new HttpHeaderSerializer(), + ) { - $this->serializer = new HttpHeaderSerializer(); } public function onKernelResponse(ResponseEvent $event): void