vendor/contao/core-bundle/src/EventListener/MergeHttpHeadersListener.php line 63

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\EventListener;
  11. use Contao\CoreBundle\Framework\ContaoFramework;
  12. use Contao\CoreBundle\HttpKernel\Header\HeaderStorageInterface;
  13. use Contao\CoreBundle\HttpKernel\Header\NativeHeaderStorage;
  14. use Symfony\Component\HttpFoundation\Cookie;
  15. use Symfony\Component\HttpFoundation\Response;
  16. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  17. use Symfony\Contracts\Service\ResetInterface;
  18. /**
  19.  * @internal
  20.  */
  21. class MergeHttpHeadersListener implements ResetInterface
  22. {
  23.     /**
  24.      * @var ContaoFramework
  25.      */
  26.     private $framework;
  27.     /**
  28.      * @var HeaderStorageInterface
  29.      */
  30.     private $headerStorage;
  31.     /**
  32.      * @var array
  33.      */
  34.     private $headers = [];
  35.     /**
  36.      * @var array
  37.      */
  38.     private $multiHeaders = [
  39.         'set-cookie',
  40.         'link',
  41.         'vary',
  42.         'pragma',
  43.         'cache-control',
  44.     ];
  45.     public function __construct(ContaoFramework $frameworkHeaderStorageInterface $headerStorage null)
  46.     {
  47.         $this->framework $framework;
  48.         $this->headerStorage $headerStorage ?: new NativeHeaderStorage();
  49.     }
  50.     /**
  51.      * Adds the Contao headers to the Symfony response.
  52.      */
  53.     public function __invoke(ResponseEvent $event): void
  54.     {
  55.         if (!$this->framework->isInitialized()) {
  56.             return;
  57.         }
  58.         // Fetch remaining headers and add them to the response
  59.         $this->fetchHttpHeaders();
  60.         $this->setResponseHeaders($event->getResponse());
  61.     }
  62.     /**
  63.      * @return array<string>
  64.      */
  65.     public function getMultiHeaders(): array
  66.     {
  67.         return array_values($this->multiHeaders);
  68.     }
  69.     public function setMultiHeader(array $headers): void
  70.     {
  71.         $this->multiHeaders $headers;
  72.     }
  73.     public function addMultiHeader(string $name): void
  74.     {
  75.         $uniqueKey $this->getUniqueKey($name);
  76.         if (!\in_array($uniqueKey$this->multiHeaderstrue)) {
  77.             $this->multiHeaders[] = $uniqueKey;
  78.         }
  79.     }
  80.     public function removeMultiHeader(string $name): void
  81.     {
  82.         if (false !== ($i array_search($this->getUniqueKey($name), $this->multiHeaderstrue))) {
  83.             unset($this->multiHeaders[$i]);
  84.         }
  85.     }
  86.     public function reset(): void
  87.     {
  88.         $this->headers = [];
  89.     }
  90.     /**
  91.      * Fetches and stores HTTP headers from PHP.
  92.      */
  93.     private function fetchHttpHeaders(): void
  94.     {
  95.         $this->headers array_merge($this->headers$this->headerStorage->all());
  96.         $this->headerStorage->clear();
  97.     }
  98.     private function setResponseHeaders(Response $response): void
  99.     {
  100.         $allowOverrides = [];
  101.         foreach ($this->headers as $header) {
  102.             if (preg_match('/^HTTP\/[^ ]+ (\d{3})( (.+))?$/i'$header$matches)) {
  103.                 $response->setStatusCode((int) $matches[1], $matches[3] ?? '');
  104.                 continue;
  105.             }
  106.             [$name$content] = explode(':'$header2);
  107.             $uniqueKey $this->getUniqueKey($name);
  108.             // Never merge cache-control headers (see #1246)
  109.             if ('cache-control' === $uniqueKey) {
  110.                 continue;
  111.             }
  112.             if ('set-cookie' === $uniqueKey) {
  113.                 $cookie Cookie::fromString($content);
  114.                 if (session_name() === $cookie->getName()) {
  115.                     $this->headerStorage->add('Set-Cookie: '.$cookie);
  116.                     continue;
  117.                 }
  118.             }
  119.             if (\in_array($uniqueKey$this->multiHeaderstrue)) {
  120.                 $response->headers->set($uniqueKeytrim($content), false);
  121.             } elseif (isset($allowOverrides[$uniqueKey]) || !$response->headers->has($uniqueKey)) {
  122.                 $allowOverrides[$uniqueKey] = true;
  123.                 $response->headers->set($uniqueKeytrim($content));
  124.             }
  125.         }
  126.     }
  127.     private function getUniqueKey(string $name): string
  128.     {
  129.         return str_replace('_''-'strtolower($name));
  130.     }
  131. }