WebProfilerExtension.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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\Twig;
  11. use Symfony\Component\VarDumper\Cloner\Data;
  12. use Symfony\Component\VarDumper\Dumper\HtmlDumper;
  13. use Twig\Environment;
  14. use Twig\Extension\ProfilerExtension;
  15. use Twig\Profiler\Profile;
  16. use Twig\TwigFunction;
  17. /**
  18. * Twig extension for the profiler.
  19. *
  20. * @author Fabien Potencier <fabien@symfony.com>
  21. *
  22. * @internal
  23. */
  24. class WebProfilerExtension extends ProfilerExtension
  25. {
  26. /**
  27. * @var HtmlDumper
  28. */
  29. private $dumper;
  30. /**
  31. * @var resource
  32. */
  33. private $output;
  34. /**
  35. * @var int
  36. */
  37. private $stackLevel = 0;
  38. public function __construct(HtmlDumper $dumper = null)
  39. {
  40. $this->dumper = $dumper ?: new HtmlDumper();
  41. $this->dumper->setOutput($this->output = fopen('php://memory', 'r+'));
  42. }
  43. public function enter(Profile $profile): void
  44. {
  45. ++$this->stackLevel;
  46. }
  47. public function leave(Profile $profile): void
  48. {
  49. if (0 === --$this->stackLevel) {
  50. $this->dumper->setOutput($this->output = fopen('php://memory', 'r+'));
  51. }
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public function getFunctions(): array
  57. {
  58. return [
  59. new TwigFunction('profiler_dump', [$this, 'dumpData'], ['is_safe' => ['html'], 'needs_environment' => true]),
  60. new TwigFunction('profiler_dump_log', [$this, 'dumpLog'], ['is_safe' => ['html'], 'needs_environment' => true]),
  61. ];
  62. }
  63. public function dumpData(Environment $env, Data $data, int $maxDepth = 0)
  64. {
  65. $this->dumper->setCharset($env->getCharset());
  66. $this->dumper->dump($data, null, [
  67. 'maxDepth' => $maxDepth,
  68. ]);
  69. $dump = stream_get_contents($this->output, -1, 0);
  70. rewind($this->output);
  71. ftruncate($this->output, 0);
  72. return str_replace("\n</pre", '</pre', rtrim($dump));
  73. }
  74. public function dumpLog(Environment $env, string $message, Data $context = null)
  75. {
  76. $message = twig_escape_filter($env, $message);
  77. $message = preg_replace('/&quot;(.*?)&quot;/', '&quot;<b>$1</b>&quot;', $message);
  78. if (null === $context || false === strpos($message, '{')) {
  79. return '<span class="dump-inline">'.$message.'</span>';
  80. }
  81. $replacements = [];
  82. foreach ($context as $k => $v) {
  83. $k = '{'.twig_escape_filter($env, $k).'}';
  84. $replacements['&quot;<b>'.$k.'</b>&quot;'] = $replacements['&quot;'.$k.'&quot;'] = $replacements[$k] = $this->dumpData($env, $v);
  85. }
  86. return '<span class="dump-inline">'.strtr($message, $replacements).'</span>';
  87. }
  88. /**
  89. * {@inheritdoc}
  90. */
  91. public function getName()
  92. {
  93. return 'profiler';
  94. }
  95. }