vendor/doctrine/doctrine-bundle/Twig/DoctrineExtension.php line 40

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Bundle\DoctrineBundle\Twig;
  3. use Doctrine\SqlFormatter\HtmlHighlighter;
  4. use Doctrine\SqlFormatter\NullHighlighter;
  5. use Doctrine\SqlFormatter\SqlFormatter;
  6. use Symfony\Component\VarDumper\Cloner\Data;
  7. use Twig\Extension\AbstractExtension;
  8. use Twig\TwigFilter;
  9. use function addslashes;
  10. use function array_key_exists;
  11. use function bin2hex;
  12. use function implode;
  13. use function is_array;
  14. use function is_bool;
  15. use function is_object;
  16. use function is_string;
  17. use function method_exists;
  18. use function preg_match;
  19. use function preg_replace_callback;
  20. use function sprintf;
  21. use function strtoupper;
  22. use function substr;
  23. use function trigger_deprecation;
  24. /**
  25. * This class contains the needed functions in order to do the query highlighting
  26. */
  27. class DoctrineExtension extends AbstractExtension
  28. {
  29. private SqlFormatter $sqlFormatter;
  30. /**
  31. * Define our functions
  32. *
  33. * @return TwigFilter[]
  34. */
  35. public function getFilters()
  36. {
  37. return [
  38. new TwigFilter('doctrine_pretty_query', [$this, 'formatQuery'], ['is_safe' => ['html'], 'deprecated' => true]),
  39. new TwigFilter('doctrine_prettify_sql', [$this, 'prettifySql'], ['is_safe' => ['html']]),
  40. new TwigFilter('doctrine_format_sql', [$this, 'formatSql'], ['is_safe' => ['html']]),
  41. new TwigFilter('doctrine_replace_query_parameters', [$this, 'replaceQueryParameters']),
  42. ];
  43. }
  44. /**
  45. * Escape parameters of a SQL query
  46. * DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE
  47. *
  48. * @internal
  49. *
  50. * @param mixed $parameter
  51. *
  52. * @return string
  53. */
  54. public static function escapeFunction($parameter)
  55. {
  56. $result = $parameter;
  57. switch (true) {
  58. // Check if result is non-unicode string using PCRE_UTF8 modifier
  59. case is_string($result) && ! preg_match('//u', $result):
  60. $result = '0x' . strtoupper(bin2hex($result));
  61. break;
  62. case is_string($result):
  63. $result = "'" . addslashes($result) . "'";
  64. break;
  65. case is_array($result):
  66. foreach ($result as &$value) {
  67. $value = static::escapeFunction($value);
  68. }
  69. $result = implode(', ', $result) ?: 'NULL';
  70. break;
  71. case is_object($result) && method_exists($result, '__toString'):
  72. $result = addslashes($result->__toString());
  73. break;
  74. case $result === null:
  75. $result = 'NULL';
  76. break;
  77. case is_bool($result):
  78. $result = $result ? '1' : '0';
  79. break;
  80. }
  81. return $result;
  82. }
  83. /**
  84. * Return a query with the parameters replaced
  85. *
  86. * @param string $query
  87. * @param mixed[]|Data $parameters
  88. *
  89. * @return string
  90. */
  91. public function replaceQueryParameters($query, $parameters)
  92. {
  93. if ($parameters instanceof Data) {
  94. $parameters = $parameters->getValue(true);
  95. }
  96. $i = 0;
  97. if (! array_key_exists(0, $parameters) && array_key_exists(1, $parameters)) {
  98. $i = 1;
  99. }
  100. return preg_replace_callback(
  101. '/\?|((?<!:):[a-z0-9_]+)/i',
  102. static function ($matches) use ($parameters, &$i) {
  103. $key = substr($matches[0], 1);
  104. if (! array_key_exists($i, $parameters) && ($key === false || ! array_key_exists($key, $parameters))) {
  105. return $matches[0];
  106. }
  107. $value = array_key_exists($i, $parameters) ? $parameters[$i] : $parameters[$key];
  108. $result = DoctrineExtension::escapeFunction($value);
  109. $i++;
  110. return $result;
  111. },
  112. $query,
  113. );
  114. }
  115. /**
  116. * Formats and/or highlights the given SQL statement.
  117. *
  118. * @param string $sql
  119. * @param bool $highlightOnly If true the query is not formatted, just highlighted
  120. *
  121. * @return string
  122. */
  123. public function formatQuery($sql, $highlightOnly = false)
  124. {
  125. trigger_deprecation(
  126. 'doctrine/doctrine-bundle',
  127. '2.1',
  128. 'The "%s()" method is deprecated and will be removed in doctrine-bundle 3.0.',
  129. __METHOD__,
  130. );
  131. $this->setUpSqlFormatter(true, true);
  132. if ($highlightOnly) {
  133. return $this->sqlFormatter->highlight($sql);
  134. }
  135. return sprintf(
  136. '<div class="highlight highlight-sql"><pre>%s</pre></div>',
  137. $this->sqlFormatter->format($sql),
  138. );
  139. }
  140. public function prettifySql(string $sql): string
  141. {
  142. $this->setUpSqlFormatter();
  143. return $this->sqlFormatter->highlight($sql);
  144. }
  145. public function formatSql(string $sql, bool $highlight): string
  146. {
  147. $this->setUpSqlFormatter($highlight);
  148. return $this->sqlFormatter->format($sql);
  149. }
  150. private function setUpSqlFormatter(bool $highlight = true, bool $legacy = false): void
  151. {
  152. $this->sqlFormatter = new SqlFormatter($highlight ? new HtmlHighlighter([
  153. HtmlHighlighter::HIGHLIGHT_PRE => 'class="highlight highlight-sql"',
  154. HtmlHighlighter::HIGHLIGHT_QUOTE => 'class="string"',
  155. HtmlHighlighter::HIGHLIGHT_BACKTICK_QUOTE => 'class="string"',
  156. HtmlHighlighter::HIGHLIGHT_RESERVED => 'class="keyword"',
  157. HtmlHighlighter::HIGHLIGHT_BOUNDARY => 'class="symbol"',
  158. HtmlHighlighter::HIGHLIGHT_NUMBER => 'class="number"',
  159. HtmlHighlighter::HIGHLIGHT_WORD => 'class="word"',
  160. HtmlHighlighter::HIGHLIGHT_ERROR => 'class="error"',
  161. HtmlHighlighter::HIGHLIGHT_COMMENT => 'class="comment"',
  162. HtmlHighlighter::HIGHLIGHT_VARIABLE => 'class="variable"',
  163. ], ! $legacy) : new NullHighlighter());
  164. }
  165. /**
  166. * Get the name of the extension
  167. *
  168. * @return string
  169. */
  170. public function getName()
  171. {
  172. return 'doctrine_extension';
  173. }
  174. }