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

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