vendor/symfony/twig-bridge/DataCollector/TwigDataCollector.php line 41

  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\Bridge\Twig\DataCollector;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  14. use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
  15. use Twig\Environment;
  16. use Twig\Error\LoaderError;
  17. use Twig\Markup;
  18. use Twig\Profiler\Dumper\HtmlDumper;
  19. use Twig\Profiler\Profile;
  20. /**
  21.  * @author Fabien Potencier <fabien@symfony.com>
  22.  *
  23.  * @final
  24.  */
  25. class TwigDataCollector extends DataCollector implements LateDataCollectorInterface
  26. {
  27.     private Profile $profile;
  28.     private ?Environment $twig;
  29.     private array $computed;
  30.     public function __construct(Profile $profileEnvironment $twig null)
  31.     {
  32.         $this->profile $profile;
  33.         $this->twig $twig;
  34.     }
  35.     public function collect(Request $requestResponse $response\Throwable $exception null)
  36.     {
  37.     }
  38.     public function reset()
  39.     {
  40.         $this->profile->reset();
  41.         unset($this->computed);
  42.         $this->data = [];
  43.     }
  44.     public function lateCollect()
  45.     {
  46.         $this->data['profile'] = serialize($this->profile);
  47.         $this->data['template_paths'] = [];
  48.         if (null === $this->twig) {
  49.             return;
  50.         }
  51.         $templateFinder = function (Profile $profile) use (&$templateFinder) {
  52.             if ($profile->isTemplate()) {
  53.                 try {
  54.                     $template $this->twig->load($name $profile->getName());
  55.                 } catch (LoaderError) {
  56.                     $template null;
  57.                 }
  58.                 if (null !== $template && '' !== $path $template->getSourceContext()->getPath()) {
  59.                     $this->data['template_paths'][$name] = $path;
  60.                 }
  61.             }
  62.             foreach ($profile as $p) {
  63.                 $templateFinder($p);
  64.             }
  65.         };
  66.         $templateFinder($this->profile);
  67.     }
  68.     public function getTime()
  69.     {
  70.         return $this->getProfile()->getDuration() * 1000;
  71.     }
  72.     public function getTemplateCount()
  73.     {
  74.         return $this->getComputedData('template_count');
  75.     }
  76.     public function getTemplatePaths()
  77.     {
  78.         return $this->data['template_paths'];
  79.     }
  80.     public function getTemplates()
  81.     {
  82.         return $this->getComputedData('templates');
  83.     }
  84.     public function getBlockCount()
  85.     {
  86.         return $this->getComputedData('block_count');
  87.     }
  88.     public function getMacroCount()
  89.     {
  90.         return $this->getComputedData('macro_count');
  91.     }
  92.     public function getHtmlCallGraph()
  93.     {
  94.         $dumper = new HtmlDumper();
  95.         $dump $dumper->dump($this->getProfile());
  96.         // needed to remove the hardcoded CSS styles
  97.         $dump str_replace([
  98.             '<span style="background-color: #ffd">',
  99.             '<span style="color: #d44">',
  100.             '<span style="background-color: #dfd">',
  101.             '<span style="background-color: #ddf">',
  102.         ], [
  103.             '<span class="status-warning">',
  104.             '<span class="status-error">',
  105.             '<span class="status-success">',
  106.             '<span class="status-info">',
  107.         ], $dump);
  108.         return new Markup($dump'UTF-8');
  109.     }
  110.     public function getProfile()
  111.     {
  112.         return $this->profile ??= unserialize($this->data['profile'], ['allowed_classes' => ['Twig_Profiler_Profile'Profile::class]]);
  113.     }
  114.     private function getComputedData(string $index)
  115.     {
  116.         $this->computed ??= $this->computeData($this->getProfile());
  117.         return $this->computed[$index];
  118.     }
  119.     private function computeData(Profile $profile)
  120.     {
  121.         $data = [
  122.             'template_count' => 0,
  123.             'block_count' => 0,
  124.             'macro_count' => 0,
  125.         ];
  126.         $templates = [];
  127.         foreach ($profile as $p) {
  128.             $d $this->computeData($p);
  129.             $data['template_count'] += ($p->isTemplate() ? 0) + $d['template_count'];
  130.             $data['block_count'] += ($p->isBlock() ? 0) + $d['block_count'];
  131.             $data['macro_count'] += ($p->isMacro() ? 0) + $d['macro_count'];
  132.             if ($p->isTemplate()) {
  133.                 if (!isset($templates[$p->getTemplate()])) {
  134.                     $templates[$p->getTemplate()] = 1;
  135.                 } else {
  136.                     ++$templates[$p->getTemplate()];
  137.                 }
  138.             }
  139.             foreach ($d['templates'] as $template => $count) {
  140.                 if (!isset($templates[$template])) {
  141.                     $templates[$template] = $count;
  142.                 } else {
  143.                     $templates[$template] += $count;
  144.                 }
  145.             }
  146.         }
  147.         $data['templates'] = $templates;
  148.         return $data;
  149.     }
  150.     public function getName(): string
  151.     {
  152.         return 'twig';
  153.     }
  154. }