vendor/symfony/web-profiler-bundle/Controller/ProfilerController.php line 245

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\Controller;
  11. use Symfony\Bundle\FullStack;
  12. use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
  13. use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager;
  14. use Symfony\Component\HttpFoundation\RedirectResponse;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
  18. use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
  19. use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
  20. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  21. use Symfony\Component\HttpKernel\Profiler\Profiler;
  22. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  23. use Twig\Environment;
  24. /**
  25.  * @author Fabien Potencier <fabien@symfony.com>
  26.  *
  27.  * @internal
  28.  */
  29. class ProfilerController
  30. {
  31.     private $templateManager;
  32.     private $generator;
  33.     private $profiler;
  34.     private $twig;
  35.     private $templates;
  36.     private $cspHandler;
  37.     private $baseDir;
  38.     public function __construct(UrlGeneratorInterface $generatorProfiler $profiler nullEnvironment $twig, array $templatesContentSecurityPolicyHandler $cspHandler nullstring $baseDir null)
  39.     {
  40.         $this->generator $generator;
  41.         $this->profiler $profiler;
  42.         $this->twig $twig;
  43.         $this->templates $templates;
  44.         $this->cspHandler $cspHandler;
  45.         $this->baseDir $baseDir;
  46.     }
  47.     /**
  48.      * Redirects to the last profiles.
  49.      *
  50.      * @throws NotFoundHttpException
  51.      */
  52.     public function homeAction(): RedirectResponse
  53.     {
  54.         $this->denyAccessIfProfilerDisabled();
  55.         return new RedirectResponse($this->generator->generate('_profiler_search_results', ['token' => 'empty''limit' => 10]), 302, ['Content-Type' => 'text/html']);
  56.     }
  57.     /**
  58.      * Renders a profiler panel for the given token.
  59.      *
  60.      * @throws NotFoundHttpException
  61.      */
  62.     public function panelAction(Request $requeststring $token): Response
  63.     {
  64.         $this->denyAccessIfProfilerDisabled();
  65.         if (null !== $this->cspHandler) {
  66.             $this->cspHandler->disableCsp();
  67.         }
  68.         $panel $request->query->get('panel');
  69.         $page $request->query->get('page''home');
  70.         if ('latest' === $token && $latest current($this->profiler->find(nullnull1nullnullnull))) {
  71.             $token $latest['token'];
  72.         }
  73.         if (!$profile $this->profiler->loadProfile($token)) {
  74.             return $this->renderWithCspNonces($request'@WebProfiler/Profiler/info.html.twig', ['about' => 'no_token''token' => $token'request' => $request]);
  75.         }
  76.         if (null === $panel) {
  77.             $panel 'request';
  78.             foreach ($profile->getCollectors() as $collector) {
  79.                 if ($collector instanceof ExceptionDataCollector && $collector->hasException()) {
  80.                     $panel $collector->getName();
  81.                     break;
  82.                 }
  83.                 if ($collector instanceof DumpDataCollector && $collector->getDumpsCount() > 0) {
  84.                     $panel $collector->getName();
  85.                 }
  86.             }
  87.         }
  88.         if (!$profile->hasCollector($panel)) {
  89.             throw new NotFoundHttpException(sprintf('Panel "%s" is not available for token "%s".'$panel$token));
  90.         }
  91.         return $this->renderWithCspNonces($request$this->getTemplateManager()->getName($profile$panel), [
  92.             'token' => $token,
  93.             'profile' => $profile,
  94.             'collector' => $profile->getCollector($panel),
  95.             'panel' => $panel,
  96.             'page' => $page,
  97.             'request' => $request,
  98.             'templates' => $this->getTemplateManager()->getNames($profile),
  99.             'is_ajax' => $request->isXmlHttpRequest(),
  100.             'profiler_markup_version' => 2// 1 = original profiler, 2 = Symfony 2.8+ profiler
  101.         ]);
  102.     }
  103.     /**
  104.      * Renders the Web Debug Toolbar.
  105.      *
  106.      * @throws NotFoundHttpException
  107.      */
  108.     public function toolbarAction(Request $requeststring $token null): Response
  109.     {
  110.         if (null === $this->profiler) {
  111.             throw new NotFoundHttpException('The profiler must be enabled.');
  112.         }
  113.         if ($request->hasSession() && ($session $request->getSession())->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag) {
  114.             // keep current flashes for one more request if using AutoExpireFlashBag
  115.             $session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
  116.         }
  117.         if ('empty' === $token || null === $token) {
  118.             return new Response(''200, ['Content-Type' => 'text/html']);
  119.         }
  120.         $this->profiler->disable();
  121.         if (!$profile $this->profiler->loadProfile($token)) {
  122.             return new Response(''404, ['Content-Type' => 'text/html']);
  123.         }
  124.         $url null;
  125.         try {
  126.             $url $this->generator->generate('_profiler', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL);
  127.         } catch (\Exception $e) {
  128.             // the profiler is not enabled
  129.         }
  130.         return $this->renderWithCspNonces($request'@WebProfiler/Profiler/toolbar.html.twig', [
  131.             'full_stack' => class_exists(FullStack::class),
  132.             'request' => $request,
  133.             'profile' => $profile,
  134.             'templates' => $this->getTemplateManager()->getNames($profile),
  135.             'profiler_url' => $url,
  136.             'token' => $token,
  137.             'profiler_markup_version' => 2// 1 = original toolbar, 2 = Symfony 2.8+ toolbar
  138.         ]);
  139.     }
  140.     /**
  141.      * Renders the profiler search bar.
  142.      *
  143.      * @throws NotFoundHttpException
  144.      */
  145.     public function searchBarAction(Request $request): Response
  146.     {
  147.         $this->denyAccessIfProfilerDisabled();
  148.         if (null !== $this->cspHandler) {
  149.             $this->cspHandler->disableCsp();
  150.         }
  151.         if (!$request->hasSession()) {
  152.             $ip =
  153.             $method =
  154.             $statusCode =
  155.             $url =
  156.             $start =
  157.             $end =
  158.             $limit =
  159.             $token null;
  160.         } else {
  161.             $session $request->getSession();
  162.             $ip $request->query->get('ip'$session->get('_profiler_search_ip'));
  163.             $method $request->query->get('method'$session->get('_profiler_search_method'));
  164.             $statusCode $request->query->get('status_code'$session->get('_profiler_search_status_code'));
  165.             $url $request->query->get('url'$session->get('_profiler_search_url'));
  166.             $start $request->query->get('start'$session->get('_profiler_search_start'));
  167.             $end $request->query->get('end'$session->get('_profiler_search_end'));
  168.             $limit $request->query->get('limit'$session->get('_profiler_search_limit'));
  169.             $token $request->query->get('token'$session->get('_profiler_search_token'));
  170.         }
  171.         return new Response(
  172.             $this->twig->render('@WebProfiler/Profiler/search.html.twig', [
  173.                 'token' => $token,
  174.                 'ip' => $ip,
  175.                 'method' => $method,
  176.                 'status_code' => $statusCode,
  177.                 'url' => $url,
  178.                 'start' => $start,
  179.                 'end' => $end,
  180.                 'limit' => $limit,
  181.                 'request' => $request,
  182.             ]),
  183.             200,
  184.             ['Content-Type' => 'text/html']
  185.         );
  186.     }
  187.     /**
  188.      * Renders the search results.
  189.      *
  190.      * @throws NotFoundHttpException
  191.      */
  192.     public function searchResultsAction(Request $requeststring $token): Response
  193.     {
  194.         $this->denyAccessIfProfilerDisabled();
  195.         if (null !== $this->cspHandler) {
  196.             $this->cspHandler->disableCsp();
  197.         }
  198.         $profile $this->profiler->loadProfile($token);
  199.         $ip $request->query->get('ip');
  200.         $method $request->query->get('method');
  201.         $statusCode $request->query->get('status_code');
  202.         $url $request->query->get('url');
  203.         $start $request->query->get('start'null);
  204.         $end $request->query->get('end'null);
  205.         $limit $request->query->get('limit');
  206.         return $this->renderWithCspNonces($request'@WebProfiler/Profiler/results.html.twig', [
  207.             'request' => $request,
  208.             'token' => $token,
  209.             'profile' => $profile,
  210.             'tokens' => $this->profiler->find($ip$url$limit$method$start$end$statusCode),
  211.             'ip' => $ip,
  212.             'method' => $method,
  213.             'status_code' => $statusCode,
  214.             'url' => $url,
  215.             'start' => $start,
  216.             'end' => $end,
  217.             'limit' => $limit,
  218.             'panel' => null,
  219.         ]);
  220.     }
  221.     /**
  222.      * Narrows the search bar.
  223.      *
  224.      * @throws NotFoundHttpException
  225.      */
  226.     public function searchAction(Request $request): Response
  227.     {
  228.         $this->denyAccessIfProfilerDisabled();
  229.         $ip $request->query->get('ip');
  230.         $method $request->query->get('method');
  231.         $statusCode $request->query->get('status_code');
  232.         $url $request->query->get('url');
  233.         $start $request->query->get('start'null);
  234.         $end $request->query->get('end'null);
  235.         $limit $request->query->get('limit');
  236.         $token $request->query->get('token');
  237.         if ($request->hasSession()) {
  238.             $session $request->getSession();
  239.             $session->set('_profiler_search_ip'$ip);
  240.             $session->set('_profiler_search_method'$method);
  241.             $session->set('_profiler_search_status_code'$statusCode);
  242.             $session->set('_profiler_search_url'$url);
  243.             $session->set('_profiler_search_start'$start);
  244.             $session->set('_profiler_search_end'$end);
  245.             $session->set('_profiler_search_limit'$limit);
  246.             $session->set('_profiler_search_token'$token);
  247.         }
  248.         if (!empty($token)) {
  249.             return new RedirectResponse($this->generator->generate('_profiler', ['token' => $token]), 302, ['Content-Type' => 'text/html']);
  250.         }
  251.         $tokens $this->profiler->find($ip$url$limit$method$start$end$statusCode);
  252.         return new RedirectResponse($this->generator->generate('_profiler_search_results', [
  253.             'token' => $tokens $tokens[0]['token'] : 'empty',
  254.             'ip' => $ip,
  255.             'method' => $method,
  256.             'status_code' => $statusCode,
  257.             'url' => $url,
  258.             'start' => $start,
  259.             'end' => $end,
  260.             'limit' => $limit,
  261.         ]), 302, ['Content-Type' => 'text/html']);
  262.     }
  263.     /**
  264.      * Displays the PHP info.
  265.      *
  266.      * @throws NotFoundHttpException
  267.      */
  268.     public function phpinfoAction(): Response
  269.     {
  270.         $this->denyAccessIfProfilerDisabled();
  271.         if (null !== $this->cspHandler) {
  272.             $this->cspHandler->disableCsp();
  273.         }
  274.         ob_start();
  275.         phpinfo();
  276.         $phpinfo ob_get_clean();
  277.         return new Response($phpinfo200, ['Content-Type' => 'text/html']);
  278.     }
  279.     /**
  280.      * Displays the source of a file.
  281.      *
  282.      * @throws NotFoundHttpException
  283.      */
  284.     public function openAction(Request $request): Response
  285.     {
  286.         if (null === $this->baseDir) {
  287.             throw new NotFoundHttpException('The base dir should be set.');
  288.         }
  289.         if ($this->profiler) {
  290.             $this->profiler->disable();
  291.         }
  292.         $file $request->query->get('file');
  293.         $line $request->query->get('line');
  294.         $filename $this->baseDir.\DIRECTORY_SEPARATOR.$file;
  295.         if (preg_match("'(^|[/\\\\])\.'"$file) || !is_readable($filename)) {
  296.             throw new NotFoundHttpException(sprintf('The file "%s" cannot be opened.'$file));
  297.         }
  298.         return $this->renderWithCspNonces($request'@WebProfiler/Profiler/open.html.twig', [
  299.             'filename' => $filename,
  300.             'file' => $file,
  301.             'line' => $line,
  302.         ]);
  303.     }
  304.     /**
  305.      * Gets the Template Manager.
  306.      */
  307.     protected function getTemplateManager(): TemplateManager
  308.     {
  309.         if (null === $this->templateManager) {
  310.             $this->templateManager = new TemplateManager($this->profiler$this->twig$this->templates);
  311.         }
  312.         return $this->templateManager;
  313.     }
  314.     private function denyAccessIfProfilerDisabled()
  315.     {
  316.         if (null === $this->profiler) {
  317.             throw new NotFoundHttpException('The profiler must be enabled.');
  318.         }
  319.         $this->profiler->disable();
  320.     }
  321.     private function renderWithCspNonces(Request $requeststring $template, array $variablesint $code 200, array $headers = ['Content-Type' => 'text/html']): Response
  322.     {
  323.         $response = new Response(''$code$headers);
  324.         $nonces $this->cspHandler $this->cspHandler->getNonces($request$response) : [];
  325.         $variables['csp_script_nonce'] = $nonces['csp_script_nonce'] ?? null;
  326.         $variables['csp_style_nonce'] = $nonces['csp_style_nonce'] ?? null;
  327.         $response->setContent($this->twig->render($template$variables));
  328.         return $response;
  329.     }
  330. }