PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Twig/DoctrineExtension.php

https://github.com/nattaphat/hgis
PHP | 322 lines | 172 code | 59 blank | 91 comment | 19 complexity | 7b7dd0b7a99d5c9dda9f2583713f439a MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Doctrine Bundle
  4. *
  5. * The code was originally distributed inside the Symfony framework.
  6. *
  7. * (c) Fabien Potencier <fabien@symfony.com>
  8. * (c) Doctrine Project, Benjamin Eberlei <kontakt@beberlei.de>
  9. *
  10. * For the full copyright and license information, please view the LICENSE
  11. * file that was distributed with this source code.
  12. */
  13. namespace Doctrine\Bundle\DoctrineBundle\Twig;
  14. use Symfony\Component\DependencyInjection\ContainerInterface;
  15. /**
  16. * This class contains the needed functions in order to do the query highlighting
  17. *
  18. * @author Florin Patan <florinpatan@gmail.com>
  19. * @author Christophe Coevoet <stof@notk.org>
  20. */
  21. class DoctrineExtension extends \Twig_Extension
  22. {
  23. /**
  24. * Number of maximum characters that one single line can hold in the interface
  25. *
  26. * @var int
  27. */
  28. private $maxCharWidth = 100;
  29. /**
  30. * Define our functions
  31. *
  32. * @return array
  33. */
  34. public function getFilters()
  35. {
  36. return array(
  37. 'doctrine_minify_query' => new \Twig_Filter_Method($this, 'minifyQuery'),
  38. 'doctrine_pretty_query' => new \Twig_Filter_Function('SqlFormatter::format'),
  39. 'doctrine_replace_query_parameters' => new \Twig_Filter_Method($this, 'replaceQueryParameters'),
  40. );
  41. }
  42. /**
  43. * Get the possible combinations of elements from the given array
  44. *
  45. * @param array $elements
  46. * @param integer $combinationsLevel
  47. *
  48. * @return array
  49. */
  50. private function getPossibleCombinations($elements, $combinationsLevel)
  51. {
  52. $baseCount = count($elements);
  53. $result = array();
  54. if ($combinationsLevel == 1) {
  55. foreach ($elements as $element) {
  56. $result[] = array($element);
  57. }
  58. return $result;
  59. }
  60. $nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1);
  61. foreach ($nextLevelElements as $nextLevelElement) {
  62. $lastElement = $nextLevelElement[$combinationsLevel - 2];
  63. $found = false;
  64. foreach ($elements as $key => $element) {
  65. if ($element == $lastElement) {
  66. $found = true;
  67. continue;
  68. }
  69. if ($found == true && $key < $baseCount) {
  70. $tmp = $nextLevelElement;
  71. $newCombination = array_slice($tmp, 0);
  72. $newCombination[] = $element;
  73. $result[] = array_slice($newCombination, 0);
  74. }
  75. }
  76. }
  77. return $result;
  78. }
  79. /**
  80. * Shrink the values of parameters from a combination
  81. *
  82. * @param array $parameters
  83. * @param array $combination
  84. *
  85. * @return string
  86. */
  87. private function shrinkParameters($parameters, $combination)
  88. {
  89. array_shift($parameters);
  90. $result = '';
  91. $maxLength = $this->maxCharWidth;
  92. $maxLength -= count($parameters) * 5;
  93. $maxLength = $maxLength / count($parameters);
  94. foreach ($parameters as $key => $value) {
  95. $isLarger = false;
  96. if (strlen($value) > $maxLength) {
  97. $value = wordwrap($value, $maxLength, "\n", true);
  98. $value = explode("\n", $value);
  99. $value = $value[0];
  100. $isLarger = true;
  101. }
  102. $value = self::escapeFunction($value);
  103. if (!is_numeric($value)) {
  104. $value = substr($value, 1, -1);
  105. }
  106. if ($isLarger) {
  107. $value .= ' [...]';
  108. }
  109. $result .= ' ' . $combination[$key] . ' ' . $value;
  110. }
  111. return trim($result);
  112. }
  113. /**
  114. * Attempt to compose the best scenario minified query so that a user could find it without expanding it
  115. *
  116. * @param string $query
  117. * @param array $keywords
  118. * @param integer $required
  119. *
  120. * @return string
  121. */
  122. private function composeMiniQuery($query, $keywords = array(), $required = 1)
  123. {
  124. // Extract the mandatory keywords and consider the rest as optional keywords
  125. $mandatoryKeywords = array_splice($keywords, 0, $required);
  126. $combinations = array();
  127. $combinationsCount = count($keywords);
  128. // Compute all the possible combinations of keywords to match the query for
  129. while ($combinationsCount > 0) {
  130. $combinations = array_merge($combinations, $this->getPossibleCombinations($keywords, $combinationsCount));
  131. $combinationsCount--;
  132. }
  133. // Try and match the best case query pattern
  134. foreach ($combinations as $combination) {
  135. $combination = array_merge($mandatoryKeywords, $combination);
  136. $regexp = implode('(.*) ', $combination) . ' (.*)';
  137. $regexp = '/^' . $regexp . '/is';
  138. if (preg_match($regexp, $query, $matches)) {
  139. $result = $this->shrinkParameters($matches, $combination);
  140. return $result;
  141. }
  142. }
  143. // Try and match the simplest query form that contains only the mandatory keywords
  144. $regexp = implode(' (.*)', $mandatoryKeywords) . ' (.*)';
  145. $regexp = '/^' . $regexp . '/is';
  146. if (preg_match($regexp, $query, $matches)) {
  147. $result = $this->shrinkParameters($matches, $mandatoryKeywords);
  148. return $result;
  149. }
  150. // Fallback in case we didn't managed to find any good match (can we actually have that happen?!)
  151. $result = substr($query, 0, $this->maxCharWidth);
  152. return $result;
  153. }
  154. /**
  155. * Minify the query
  156. *
  157. * @param string $query
  158. *
  159. * @return string
  160. */
  161. public function minifyQuery($query)
  162. {
  163. $result = '';
  164. $keywords = array();
  165. $required = 1;
  166. // Check if we can match the query against any of the major types
  167. switch (true) {
  168. case stripos($query, 'SELECT') !== false:
  169. $keywords = array('SELECT', 'FROM', 'WHERE', 'HAVING', 'ORDER BY', 'LIMIT');
  170. $required = 2;
  171. break;
  172. case stripos($query, 'DELETE') !== false :
  173. $keywords = array('DELETE', 'FROM', 'WHERE', 'ORDER BY', 'LIMIT');
  174. $required = 2;
  175. break;
  176. case stripos($query, 'UPDATE') !== false :
  177. $keywords = array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT');
  178. $required = 2;
  179. break;
  180. case stripos($query, 'INSERT') !== false :
  181. $keywords = array('INSERT', 'INTO', 'VALUE', 'VALUES');
  182. $required = 2;
  183. break;
  184. // If there's no match so far just truncate it to the maximum allowed by the interface
  185. default:
  186. $result = substr($query, 0, $this->maxCharWidth);
  187. }
  188. // If we had a match then we should minify it
  189. if ($result == '') {
  190. $result = $this->composeMiniQuery($query, $keywords, $required);
  191. }
  192. // Remove unneeded boilerplate HTML
  193. $result = str_replace(array("<pre style='background:white;'", "</pre>"), array("<span", "</span>"), $result);
  194. return $result;
  195. }
  196. /**
  197. * Escape parameters of a SQL query
  198. * DON'T USE THIS FUNCTION OUTSIDE ITS INTEDED SCOPE
  199. *
  200. * @internal
  201. *
  202. * @param mixed $parameter
  203. *
  204. * @return string
  205. */
  206. static public function escapeFunction($parameter)
  207. {
  208. $result = $parameter;
  209. switch (true) {
  210. case is_string($result) :
  211. $result = "'" . addslashes($result) . "'";
  212. break;
  213. case is_array($result) :
  214. foreach ($result as &$value) {
  215. $value = static::escapeFunction($value);
  216. }
  217. $result = implode(', ', $result);
  218. break;
  219. case is_object($result) :
  220. $result = addslashes((string) $result);
  221. break;
  222. }
  223. return $result;
  224. }
  225. /**
  226. * Return a query with the parameters replaced
  227. *
  228. * @param string $query
  229. * @param array $parameters
  230. *
  231. * @return string
  232. */
  233. public function replaceQueryParameters($query, $parameters)
  234. {
  235. $i = 0;
  236. $result = preg_replace_callback(
  237. '/\?|(:[a-z0-9_]+)/i',
  238. function ($matches) use ($parameters, &$i) {
  239. $key = substr($matches[0], 1);
  240. if (!isset($parameters[$i]) && !isset($parameters[$key])) {
  241. return $matches[0];
  242. }
  243. $value = isset($parameters[$i]) ? $parameters[$i] : $parameters[$key];
  244. $result = DoctrineExtension::escapeFunction($value);
  245. $i++;
  246. return $result;
  247. },
  248. $query
  249. );
  250. $result = \SqlFormatter::highlight($result);
  251. $result = str_replace(array("<pre ", "</pre>"), array("<span ", "</span>"), $result);
  252. return $result;
  253. }
  254. /**
  255. * Get the name of the extension
  256. *
  257. * @return string
  258. */
  259. public function getName()
  260. {
  261. return 'doctrine_extension';
  262. }
  263. }