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

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