vendor/symfony/web-profiler-bundle/EventListener/WebDebugToolbarListener.php line 63

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bundle\WebProfilerBundle\EventListener;
  11. use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use Symfony\Component\HttpFoundation\Response;
  15. use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
  16. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  17. use Symfony\Component\HttpKernel\KernelEvents;
  18. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  19. use Twig\Environment;
  20. /**
  21.  * WebDebugToolbarListener injects the Web Debug Toolbar.
  22.  *
  23.  * The onKernelResponse method must be connected to the kernel.response event.
  24.  *
  25.  * The WDT is only injected on well-formed HTML (with a proper </body> tag).
  26.  * This means that the WDT is never included in sub-requests or ESI requests.
  27.  *
  28.  * @author Fabien Potencier <fabien@symfony.com>
  29.  *
  30.  * @final since Symfony 4.3
  31.  */
  32. class WebDebugToolbarListener implements EventSubscriberInterface
  33. {
  34.     public const DISABLED 1;
  35.     public const ENABLED 2;
  36.     protected $twig;
  37.     protected $urlGenerator;
  38.     protected $interceptRedirects;
  39.     protected $mode;
  40.     protected $excludedAjaxPaths;
  41.     private $cspHandler;
  42.     public function __construct(Environment $twigbool $interceptRedirects falseint $mode self::ENABLEDUrlGeneratorInterface $urlGenerator nullstring $excludedAjaxPaths '^/bundles|^/_wdt'ContentSecurityPolicyHandler $cspHandler null)
  43.     {
  44.         $this->twig $twig;
  45.         $this->urlGenerator $urlGenerator;
  46.         $this->interceptRedirects $interceptRedirects;
  47.         $this->mode $mode;
  48.         $this->excludedAjaxPaths $excludedAjaxPaths;
  49.         $this->cspHandler $cspHandler;
  50.     }
  51.     public function isEnabled()
  52.     {
  53.         return self::DISABLED !== $this->mode;
  54.     }
  55.     public function onKernelResponse(FilterResponseEvent $event)
  56.     {
  57.         $response $event->getResponse();
  58.         $request $event->getRequest();
  59.         if ($response->headers->has('X-Debug-Token') && null !== $this->urlGenerator) {
  60.             try {
  61.                 $response->headers->set(
  62.                     'X-Debug-Token-Link',
  63.                     $this->urlGenerator->generate('_profiler', ['token' => $response->headers->get('X-Debug-Token')], UrlGeneratorInterface::ABSOLUTE_URL)
  64.                 );
  65.             } catch (\Exception $e) {
  66.                 $response->headers->set('X-Debug-Error', \get_class($e).': '.preg_replace('/\s+/'' '$e->getMessage()));
  67.             }
  68.         }
  69.         if (!$event->isMasterRequest()) {
  70.             return;
  71.         }
  72.         $nonces $this->cspHandler $this->cspHandler->updateResponseHeaders($request$response) : [];
  73.         // do not capture redirects or modify XML HTTP Requests
  74.         if ($request->isXmlHttpRequest()) {
  75.             return;
  76.         }
  77.         if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects && 'html' === $request->getRequestFormat()) {
  78.             if ($request->hasSession() && ($session $request->getSession())->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag) {
  79.                 // keep current flashes for one more request if using AutoExpireFlashBag
  80.                 $session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
  81.             }
  82.             $response->setContent($this->twig->render('@WebProfiler/Profiler/toolbar_redirect.html.twig', ['location' => $response->headers->get('Location')]));
  83.             $response->setStatusCode(200);
  84.             $response->headers->remove('Location');
  85.         }
  86.         if (self::DISABLED === $this->mode
  87.             || !$response->headers->has('X-Debug-Token')
  88.             || $response->isRedirection()
  89.             || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html'))
  90.             || 'html' !== $request->getRequestFormat()
  91.             || false !== stripos($response->headers->get('Content-Disposition'), 'attachment;')
  92.         ) {
  93.             return;
  94.         }
  95.         $this->injectToolbar($response$request$nonces);
  96.     }
  97.     /**
  98.      * Injects the web debug toolbar into the given Response.
  99.      */
  100.     protected function injectToolbar(Response $responseRequest $request, array $nonces)
  101.     {
  102.         $content $response->getContent();
  103.         $pos strripos($content'</body>');
  104.         if (false !== $pos) {
  105.             $toolbar "\n".str_replace("\n"''$this->twig->render(
  106.                 '@WebProfiler/Profiler/toolbar_js.html.twig',
  107.                 [
  108.                     'excluded_ajax_paths' => $this->excludedAjaxPaths,
  109.                     'token' => $response->headers->get('X-Debug-Token'),
  110.                     'request' => $request,
  111.                     'csp_script_nonce' => isset($nonces['csp_script_nonce']) ? $nonces['csp_script_nonce'] : null,
  112.                     'csp_style_nonce' => isset($nonces['csp_style_nonce']) ? $nonces['csp_style_nonce'] : null,
  113.                 ]
  114.             ))."\n";
  115.             $content substr($content0$pos).$toolbar.substr($content$pos);
  116.             $response->setContent($content);
  117.         }
  118.     }
  119.     public static function getSubscribedEvents()
  120.     {
  121.         return [
  122.             KernelEvents::RESPONSE => ['onKernelResponse', -128],
  123.         ];
  124.     }
  125. }