This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the Doctrine Bundle |
||
5 | * |
||
6 | * The code was originally distributed inside the Symfony framework. |
||
7 | * |
||
8 | * (c) Fabien Potencier <[email protected]> |
||
9 | * (c) Doctrine Project, Benjamin Eberlei <[email protected]> |
||
10 | * |
||
11 | * For the full copyright and license information, please view the LICENSE |
||
12 | * file that was distributed with this source code. |
||
13 | */ |
||
14 | |||
15 | namespace Saxulum\SaxulumWebProfiler\Twig; |
||
16 | |||
17 | /** |
||
18 | * This class contains the needed functions in order to do the query highlighting |
||
19 | * |
||
20 | * @author Florin Patan <[email protected]> |
||
21 | * @author Christophe Coevoet <[email protected]> |
||
22 | */ |
||
23 | class DoctrineExtension extends \Twig_Extension |
||
24 | { |
||
25 | /** |
||
26 | * Number of maximum characters that one single line can hold in the interface |
||
27 | * |
||
28 | * @var int |
||
29 | */ |
||
30 | private $maxCharWidth = 100; |
||
31 | |||
32 | /** |
||
33 | * Define our functions |
||
34 | * |
||
35 | * @return array |
||
36 | */ |
||
37 | public function getFilters() |
||
38 | { |
||
39 | return array( |
||
40 | new \Twig_SimpleFilter('doctrine_minify_query', array($this, 'minifyQuery')), |
||
41 | new \Twig_SimpleFilter('doctrine_pretty_query', 'SqlFormatter::format'), |
||
42 | new \Twig_SimpleFilter('doctrine_replace_query_parameters', array($this, 'replaceQueryParameters')), |
||
43 | ); |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * Get the possible combinations of elements from the given array |
||
48 | * |
||
49 | * @param array $elements |
||
50 | * @param integer $combinationsLevel |
||
51 | * |
||
52 | * @return array |
||
53 | */ |
||
54 | private function getPossibleCombinations($elements, $combinationsLevel) |
||
55 | { |
||
56 | $baseCount = count($elements); |
||
57 | $result = array(); |
||
58 | |||
59 | if ($combinationsLevel == 1) { |
||
60 | foreach ($elements as $element) { |
||
61 | $result[] = array($element); |
||
62 | } |
||
63 | |||
64 | return $result; |
||
65 | } |
||
66 | |||
67 | $nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1); |
||
68 | |||
69 | foreach ($nextLevelElements as $nextLevelElement) { |
||
70 | $lastElement = $nextLevelElement[$combinationsLevel - 2]; |
||
71 | $found = false; |
||
72 | |||
73 | foreach ($elements as $key => $element) { |
||
74 | if ($element == $lastElement) { |
||
75 | $found = true; |
||
76 | continue; |
||
77 | } |
||
78 | |||
79 | if ($found == true && $key < $baseCount) { |
||
0 ignored issues
–
show
|
|||
80 | $tmp = $nextLevelElement; |
||
81 | $newCombination = array_slice($tmp, 0); |
||
82 | $newCombination[] = $element; |
||
83 | $result[] = array_slice($newCombination, 0); |
||
84 | } |
||
85 | } |
||
86 | } |
||
87 | |||
88 | return $result; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Shrink the values of parameters from a combination |
||
93 | * |
||
94 | * @param array $parameters |
||
95 | * @param array $combination |
||
96 | * |
||
97 | * @return string |
||
98 | */ |
||
99 | private function shrinkParameters($parameters, $combination) |
||
100 | { |
||
101 | array_shift($parameters); |
||
102 | $result = ''; |
||
103 | |||
104 | $maxLength = $this->maxCharWidth; |
||
105 | $maxLength -= count($parameters) * 5; |
||
106 | $maxLength = $maxLength / count($parameters); |
||
107 | |||
108 | foreach ($parameters as $key => $value) { |
||
109 | $isLarger = false; |
||
110 | |||
111 | if (strlen($value) > $maxLength) { |
||
112 | $value = wordwrap($value, $maxLength, "\n", true); |
||
113 | $value = explode("\n", $value); |
||
114 | $value = $value[0]; |
||
115 | |||
116 | $isLarger = true; |
||
117 | } |
||
118 | $value = self::escapeFunction($value); |
||
119 | |||
120 | if (!is_numeric($value)) { |
||
121 | $value = substr($value, 1, -1); |
||
122 | } |
||
123 | |||
124 | if ($isLarger) { |
||
125 | $value .= ' [...]'; |
||
126 | } |
||
127 | |||
128 | $result .= ' ' . $combination[$key] . ' ' . $value; |
||
129 | } |
||
130 | |||
131 | return trim($result); |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Attempt to compose the best scenario minified query so that a user could find it without expanding it |
||
136 | * |
||
137 | * @param string $query |
||
138 | * @param array $keywords |
||
139 | * @param integer $required |
||
140 | * |
||
141 | * @return string |
||
142 | */ |
||
143 | private function composeMiniQuery($query, $keywords = array(), $required = 1) |
||
144 | { |
||
145 | // Extract the mandatory keywords and consider the rest as optional keywords |
||
146 | $mandatoryKeywords = array_splice($keywords, 0, $required); |
||
147 | |||
148 | $combinations = array(); |
||
149 | $combinationsCount = count($keywords); |
||
150 | |||
151 | // Compute all the possible combinations of keywords to match the query for |
||
152 | while ($combinationsCount > 0) { |
||
153 | $combinations = array_merge($combinations, $this->getPossibleCombinations($keywords, $combinationsCount)); |
||
154 | $combinationsCount--; |
||
155 | } |
||
156 | |||
157 | // Try and match the best case query pattern |
||
158 | foreach ($combinations as $combination) { |
||
159 | $combination = array_merge($mandatoryKeywords, $combination); |
||
160 | |||
161 | $regexp = implode('(.*) ', $combination) . ' (.*)'; |
||
162 | $regexp = '/^' . $regexp . '/is'; |
||
163 | |||
164 | if (preg_match($regexp, $query, $matches)) { |
||
165 | |||
166 | $result = $this->shrinkParameters($matches, $combination); |
||
167 | |||
168 | return $result; |
||
169 | } |
||
170 | } |
||
171 | |||
172 | // Try and match the simplest query form that contains only the mandatory keywords |
||
173 | $regexp = implode(' (.*)', $mandatoryKeywords) . ' (.*)'; |
||
174 | $regexp = '/^' . $regexp . '/is'; |
||
175 | |||
176 | if (preg_match($regexp, $query, $matches)) { |
||
177 | $result = $this->shrinkParameters($matches, $mandatoryKeywords); |
||
178 | |||
179 | return $result; |
||
180 | } |
||
181 | |||
182 | // Fallback in case we didn't managed to find any good match (can we actually have that happen?!) |
||
183 | $result = substr($query, 0, $this->maxCharWidth); |
||
184 | |||
185 | return $result; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Minify the query |
||
190 | * |
||
191 | * @param string $query |
||
192 | * |
||
193 | * @return string |
||
194 | */ |
||
195 | public function minifyQuery($query) |
||
196 | { |
||
197 | $result = ''; |
||
198 | $keywords = array(); |
||
199 | $required = 1; |
||
200 | |||
201 | // Check if we can match the query against any of the major types |
||
202 | switch (true) { |
||
203 | View Code Duplication | case stripos($query, 'SELECT') !== false: |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
204 | $keywords = array('SELECT', 'FROM', 'WHERE', 'HAVING', 'ORDER BY', 'LIMIT'); |
||
205 | $required = 2; |
||
206 | break; |
||
207 | |||
208 | View Code Duplication | case stripos($query, 'DELETE') !== false : |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
209 | $keywords = array('DELETE', 'FROM', 'WHERE', 'ORDER BY', 'LIMIT'); |
||
210 | $required = 2; |
||
211 | break; |
||
212 | |||
213 | View Code Duplication | case stripos($query, 'UPDATE') !== false : |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
214 | $keywords = array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'); |
||
215 | $required = 2; |
||
216 | break; |
||
217 | |||
218 | View Code Duplication | case stripos($query, 'INSERT') !== false : |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
219 | $keywords = array('INSERT', 'INTO', 'VALUE', 'VALUES'); |
||
220 | $required = 2; |
||
221 | break; |
||
222 | |||
223 | // If there's no match so far just truncate it to the maximum allowed by the interface |
||
224 | default: |
||
225 | $result = substr($query, 0, $this->maxCharWidth); |
||
226 | } |
||
227 | |||
228 | // If we had a match then we should minify it |
||
229 | if ($result == '') { |
||
230 | $result = $this->composeMiniQuery($query, $keywords, $required); |
||
231 | } |
||
232 | |||
233 | // Remove unneeded boilerplate HTML |
||
234 | $result = str_replace(array("<pre style='background:white;'", "</pre>"), array("<span", "</span>"), $result); |
||
235 | |||
236 | return $result; |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Escape parameters of a SQL query |
||
241 | * DON'T USE THIS FUNCTION OUTSIDE ITS INTEDED SCOPE |
||
242 | * |
||
243 | * @internal |
||
244 | * |
||
245 | * @param mixed $parameter |
||
246 | * |
||
247 | * @return string |
||
248 | */ |
||
249 | public static function escapeFunction($parameter) |
||
250 | { |
||
251 | $result = $parameter; |
||
252 | |||
253 | switch (true) { |
||
254 | case is_string($result) : |
||
255 | $result = "'" . addslashes($result) . "'"; |
||
256 | break; |
||
257 | |||
258 | case is_array($result) : |
||
259 | foreach ($result as &$value) { |
||
260 | $value = static::escapeFunction($value); |
||
261 | } |
||
262 | |||
263 | $result = implode(', ', $result); |
||
264 | break; |
||
265 | |||
266 | case is_object($result) : |
||
267 | $result = addslashes((string) $result); |
||
268 | break; |
||
269 | } |
||
270 | |||
271 | return $result; |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * Return a query with the parameters replaced |
||
276 | * |
||
277 | * @param string $query |
||
278 | * @param array $parameters |
||
279 | * |
||
280 | * @return string |
||
281 | */ |
||
282 | public function replaceQueryParameters($query, $parameters) |
||
283 | { |
||
284 | $i = 0; |
||
285 | |||
286 | $result = preg_replace_callback( |
||
287 | '/\?|(:[a-z0-9_]+)/i', |
||
288 | function ($matches) use ($parameters, &$i) { |
||
289 | $key = substr($matches[0], 1); |
||
290 | if (!isset($parameters[$i]) && !isset($parameters[$key])) { |
||
291 | return $matches[0]; |
||
292 | } |
||
293 | |||
294 | $value = isset($parameters[$i]) ? $parameters[$i] : $parameters[$key]; |
||
295 | $result = DoctrineExtension::escapeFunction($value); |
||
296 | $i++; |
||
297 | |||
298 | return $result; |
||
299 | }, |
||
300 | $query |
||
301 | ); |
||
302 | |||
303 | $result = \SqlFormatter::highlight($result); |
||
304 | $result = str_replace(array("<pre ", "</pre>"), array("<span ", "</span>"), $result); |
||
305 | |||
306 | return $result; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Get the name of the extension |
||
311 | * |
||
312 | * @return string |
||
313 | */ |
||
314 | public function getName() |
||
315 | { |
||
316 | return 'doctrine_extension'; |
||
317 | } |
||
318 | |||
319 | } |
||
320 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.