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 | * Created by PhpStorm. |
||
4 | * User: admin |
||
5 | * Date: 21.01.2018 |
||
6 | * Time: 14:35 |
||
7 | */ |
||
8 | |||
9 | namespace sonrac\Arango\Query\Grammars; |
||
10 | |||
11 | use Illuminate\Database\Query\Builder; |
||
12 | use Illuminate\Database\Query\Grammars\Grammar as IlluminateGrammar; |
||
13 | use function sonrac\Arango\Helpers\getEntityName; |
||
14 | use function sonrac\Arango\Helpers\getEntityNameFromColumn; |
||
15 | |||
16 | class Grammar extends IlluminateGrammar |
||
17 | { |
||
18 | /** |
||
19 | * The components that make up a select clause. |
||
20 | * |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $selectComponents = [ |
||
24 | 'aggregate', |
||
25 | 'from', |
||
26 | 'joins', |
||
27 | 'wheres', |
||
28 | 'groups', |
||
29 | 'havings', |
||
30 | 'orders', |
||
31 | 'limit', |
||
32 | 'offset', |
||
33 | 'columns', |
||
34 | 'unions', |
||
35 | 'lock', |
||
36 | ]; |
||
37 | |||
38 | /** |
||
39 | * {@inheritdoc} |
||
40 | */ |
||
41 | public function compileInsertGetId(Builder $query, $values, $sequence) |
||
42 | { |
||
43 | return $this->compileInsert($query, $values) . ' RETURN NEW'; |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * {@inheritdoc} |
||
48 | */ |
||
49 | public function compileInsert(Builder $query, array $values) |
||
50 | { |
||
51 | // Essentially we will force every insert to be treated as a batch insert which |
||
52 | // simply makes creating the SQL easier for us since we can utilize the same |
||
53 | // basic routine regardless of an amount of records given to us to insert. |
||
54 | $collection = $this->wrapTable($query->from); |
||
55 | |||
56 | $entityName = getEntityName($query->from); |
||
57 | |||
58 | if (!is_array(reset($values))) { |
||
59 | $values = [$values]; |
||
60 | } |
||
61 | |||
62 | $columns = array_keys(reset($values)); |
||
63 | |||
64 | $parameters = []; |
||
65 | |||
66 | foreach ($values as $record) { |
||
67 | $bindValuesTmp = []; |
||
68 | foreach ($columns as $column) { |
||
69 | if (!isset($record[$column])) { |
||
70 | continue; |
||
71 | } |
||
72 | $bindValuesTmp[$column] = $record[$column]; |
||
73 | } |
||
74 | $parameters[] = $bindValuesTmp; |
||
75 | } |
||
76 | $parameters = json_encode($parameters); |
||
77 | $parameters = preg_replace('/"(\@B\w+)"/', '$1', $parameters); |
||
78 | |||
79 | $aql = "FOR {$entityName} IN {$parameters} INSERT {$entityName} INTO {$collection}"; |
||
80 | return $aql; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Prepare column before use in AQL request |
||
85 | * Add entityName and wrap it if needed. |
||
86 | * Add alias for string like (column as other_name) |
||
87 | * |
||
88 | * @param $collection |
||
89 | * @param $column |
||
90 | * @param bool $withCollection |
||
91 | * @return string |
||
92 | */ |
||
93 | public function wrapColumn($column, $collection = null, $withCollection = true) |
||
94 | { |
||
95 | $entityName = getEntityNameFromColumn($column); |
||
96 | |||
97 | $clearColumn = $this->getClearColumnName($column); |
||
98 | |||
99 | $alias = $this->getAliasNameFromColumn($column); |
||
100 | |||
101 | if (is_null($entityName) && !is_null($collection)) { |
||
102 | $entityName = getEntityName($collection); |
||
103 | } |
||
104 | |||
105 | if ($clearColumn !== '_key') { |
||
106 | $clearColumn = trim($clearColumn, '`'); |
||
107 | $clearColumn = '`'.$clearColumn.'`'; |
||
108 | } |
||
109 | if ($withCollection) { |
||
110 | $column = $entityName.'.'.$clearColumn; |
||
111 | } |
||
112 | if ($alias) { |
||
0 ignored issues
–
show
|
|||
113 | $column = $alias.':'.$column; |
||
114 | } |
||
115 | return $column; |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Return collection name |
||
120 | * @return string |
||
121 | */ |
||
122 | public function wrapTable($table) |
||
123 | { |
||
124 | return $this->wrapCollection($table); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * {@inheritdoc} |
||
129 | */ |
||
130 | public function columnize(array $columns) |
||
131 | { |
||
132 | $resultColumns = []; |
||
133 | foreach ($columns as $column) { |
||
134 | if (strpos($column, ':') !== false) { |
||
135 | $resultColumns[] = $column; |
||
136 | continue; |
||
137 | } |
||
138 | |||
139 | list($entityName, $column) = explode('.', $column); |
||
140 | if ($column === '`*`') { |
||
141 | $resultColumns[] = $entityName . ': '.$entityName; |
||
142 | continue; |
||
143 | } |
||
144 | $resultColumns[] = $column . ': ' . $entityName . '.' . $column; |
||
145 | } |
||
146 | return implode(',', $resultColumns); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * {@inheritdoc} |
||
151 | */ |
||
152 | public function compileUpdate(Builder $query, $values) |
||
153 | { |
||
154 | $table = $this->wrapTable($query->from); |
||
155 | |||
156 | // Each one of the columns in the update statements needs to be wrapped in the |
||
157 | // keyword identifiers, also a place-holder needs to be created for each of |
||
158 | // the values in the list of bindings so we can make the sets statements. |
||
159 | $columns = collect($values)->map(function ($value, $key) { |
||
160 | return $key.' : '.$value; |
||
161 | })->implode(', '); |
||
162 | |||
163 | $joins = ''; |
||
164 | |||
165 | if (isset($query->joins)) { |
||
166 | $joins = $this->compileJoins($query, $query->joins).' '; |
||
167 | } |
||
168 | |||
169 | // Of course, update queries may also be constrained by where clauses so we'll |
||
170 | // need to compile the where clauses and attach it to the query so only the |
||
171 | // intended records are updated by the SQL statements we generate to run. |
||
172 | $wheres = $this->compileWheres($query); |
||
173 | |||
174 | $entityName = getEntityName($table); |
||
175 | |||
176 | $aql = $joins.'FOR '.$entityName.' IN '.$table.' '.$wheres. |
||
177 | ' UPDATE '.$entityName.' WITH { '.$columns.' } IN '.$table; |
||
178 | |||
179 | var_dump($aql); |
||
0 ignored issues
–
show
|
|||
180 | return $aql; |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * {@inheritdoc} |
||
185 | */ |
||
186 | public function compileDelete(Builder $query) |
||
187 | { |
||
188 | $wheres = is_array($query->wheres) ? $this->compileWheres($query) : ''; |
||
189 | |||
190 | $collection = $this->wrapTable($query->from); |
||
191 | $entityName = getEntityName($collection); |
||
192 | $aql = "FOR {$entityName} in {$collection} {$wheres} REMOVE {$entityName} IN {$collection}"; |
||
193 | var_dump($aql); |
||
0 ignored issues
–
show
|
|||
194 | return $aql; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * {@inheritdoc} |
||
199 | */ |
||
200 | public function compileSelect(Builder $query) |
||
201 | { |
||
202 | // If the query does not have any columns set, we'll set the columns to the |
||
203 | // * character to just get all of the columns from the database. Then we |
||
204 | // can build the query and concatenate all the pieces together as one. |
||
205 | $original = $query->columns; |
||
206 | |||
207 | if (is_null($query->columns)) { |
||
208 | $query->columns = ['*']; |
||
209 | } |
||
210 | |||
211 | // To compile the query, we'll spin through each component of the query and |
||
212 | // see if that component exists. If it does we'll just call the compiler |
||
213 | // function for the component which is responsible for making the AQL. |
||
214 | $aql = trim($this->concatenate( |
||
215 | $this->compileComponents($query)) |
||
216 | ); |
||
217 | |||
218 | if (isset($query->joins)) { |
||
219 | $aql = $this->compileJoins($query, $query->joins).' '.$aql; |
||
220 | } |
||
221 | |||
222 | if (!is_null($query->aggregate)) { |
||
223 | $aql = $this->compileAggregateExtended($query, $query->aggregate, $aql); |
||
224 | } |
||
225 | $query->columns = $original; |
||
226 | |||
227 | return $aql; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * {@inheritdoc} |
||
232 | */ |
||
233 | protected function compileJoins(Builder $query, $joins) |
||
234 | { |
||
235 | return collect($joins)->map(function ($join) use (&$aql) { |
||
236 | $table = $this->wrapTable($join->table); |
||
237 | $entityName = getEntityName($join->table); |
||
238 | return 'FOR '.$entityName.' IN '.$table; |
||
239 | })->implode(' '); |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * {@inheritdoc} |
||
244 | */ |
||
245 | protected function compileComponents(Builder $query) |
||
246 | { |
||
247 | $aql = []; |
||
248 | |||
249 | foreach ($this->selectComponents as $component) { |
||
250 | // To compile the query, we'll spin through each component of the query and |
||
251 | // see if that component exists. If it does we'll just call the compiler |
||
252 | // function for the component which is responsible for making the SQL. |
||
253 | if (!is_null($query->$component)) { |
||
254 | if ($component === 'aggregate' || |
||
255 | $component === 'joins') { |
||
256 | continue; |
||
257 | } |
||
258 | $method = 'compile'.ucfirst($component); |
||
259 | |||
260 | $aql[$component] = $this->$method($query, $query->$component); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | return $aql; |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * {@inheritdoc} |
||
269 | */ |
||
270 | protected function compileFrom(Builder $query, $collection) |
||
271 | { |
||
272 | return 'FOR '.getEntityName($collection).' IN '.$this->wrapCollection($collection); |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * {@inheritdoc} |
||
277 | */ |
||
278 | protected function compileColumns(Builder $query, $columns) |
||
279 | { |
||
280 | if (count($columns) === 1 && $columns[0] === '*') { |
||
281 | return 'RETURN '.getEntityName($query->from); |
||
282 | } |
||
283 | |||
284 | return 'RETURN { ' . $this->columnize($columns) . ' }'; |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Return string for aggregate some column from AQL request |
||
289 | * |
||
290 | * @param Builder $query |
||
291 | * @param $aggregate |
||
292 | * @param $aql |
||
293 | * @return string |
||
294 | */ |
||
295 | protected function compileAggregateExtended(Builder $query, $aggregate, $aql) |
||
0 ignored issues
–
show
|
|||
296 | { |
||
297 | return 'RETURN {"aggregate":'.$aggregate['function'].'('.$aql.')}'; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * {@inheritdoc} |
||
302 | */ |
||
303 | protected function compileWheres(Builder $query) |
||
304 | { |
||
305 | // Each type of where clauses has its own compiler function which is responsible |
||
306 | // for actually creating the where clauses SQL. This helps keep the code nice |
||
307 | // and maintainable since each clause has a very small method that it uses. |
||
308 | if (is_null($query->wheres)) { |
||
309 | return ''; |
||
310 | } |
||
311 | |||
312 | // If we actually have some where clauses, we will strip off the first boolean |
||
313 | // operator, which is added by the query builders for convenience so we can |
||
314 | // avoid checking for the first clauses in each of the compilers methods. |
||
315 | if (count($sql = $this->compileWheresToArray($query)) > 0) { |
||
316 | return $this->concatenateWhereClauses($query, $sql); |
||
317 | } |
||
318 | |||
319 | return ''; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * {@inheritdoc} |
||
324 | */ |
||
325 | protected function concatenateWhereClauses($query, $sql) |
||
326 | { |
||
327 | return 'FILTER '.$this->removeLeadingBoolean(implode(' ', $sql)); |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * {@inheritdoc} |
||
332 | */ |
||
333 | protected function compileWheresToArray($query) |
||
334 | { |
||
335 | return collect($query->wheres)->map(function ($where) use ($query) { |
||
336 | return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where); |
||
337 | })->all(); |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * {@inheritdoc} |
||
342 | */ |
||
343 | protected function whereBasic(Builder $query, $where) |
||
344 | { |
||
345 | return $where['column'].' '.$where['operator'].' '.$where['value']; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * {@inheritdoc} |
||
350 | */ |
||
351 | View Code Duplication | protected function whereIn(Builder $query, $where) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
352 | { |
||
353 | if (!empty($where['values'])) { |
||
354 | $column = $this->wrapColumn($where['column'], $query->from); |
||
355 | return '['.implode(',', $where['values']).'] ANY == '.$column; |
||
356 | } |
||
357 | |||
358 | return '0 = 1'; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * {@inheritdoc} |
||
363 | */ |
||
364 | View Code Duplication | protected function whereNotIn(Builder $query, $where) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
365 | { |
||
366 | if (!empty($where['values'])) { |
||
367 | $column = $this->wrapColumn($where['table'], $where['column']); |
||
368 | return '['.implode(',', $where['values']).'] NONE == '.$column; |
||
369 | } |
||
370 | |||
371 | return '0 = 1'; |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * {@inheritdoc} |
||
376 | */ |
||
377 | protected function whereNull(Builder $query, $where) |
||
378 | { |
||
379 | return $this->wrapColumn($where['column'], $query->from).' == NULL'; |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * {@inheritdoc} |
||
384 | */ |
||
385 | protected function whereNotNull(Builder $query, $where) |
||
386 | { |
||
387 | return $this->wrapColumn($where['column'], $query->from).' != NULL'; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * {@inheritdoc} |
||
392 | */ |
||
393 | protected function whereColumn(Builder $query, $where) |
||
394 | { |
||
395 | $firstWrapColumn = $this->wrapColumn($where['first'], $query->from); |
||
396 | $secondWrapColumn = $this->wrapColumn($where['second'], $query->from); |
||
397 | return $firstWrapColumn.' '.$where['operator'].' '.$secondWrapColumn; |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * {@inheritdoc} |
||
402 | */ |
||
403 | protected function compileOrders(Builder $query, $orders) |
||
404 | { |
||
405 | if (!empty($orders)) { |
||
406 | return 'SORT '.implode(', ', $this->compileOrdersToArray($query, $orders)); |
||
407 | } |
||
408 | |||
409 | return ''; |
||
410 | } |
||
411 | |||
412 | /** |
||
413 | * {@inheritdoc} |
||
414 | */ |
||
415 | protected function compileOrdersToArray(Builder $query, $orders) |
||
416 | { |
||
417 | return array_map(function ($order) { |
||
418 | return !isset($order['sql']) |
||
419 | ? $order['column'].' '.$order['direction'] |
||
420 | : $order['sql']; |
||
421 | }, $orders); |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * {@inheritdoc} |
||
426 | */ |
||
427 | protected function compileLimit(Builder $query, $limit) |
||
428 | { |
||
429 | $result = 'LIMIT '; |
||
430 | |||
431 | if (isset($query->offset)) { |
||
432 | $result .= (int) $query->offset.', '; |
||
433 | } |
||
434 | $result .= (int) $limit; |
||
435 | |||
436 | return $result; |
||
437 | } |
||
438 | |||
439 | /** |
||
440 | * {@inheritdoc} |
||
441 | */ |
||
442 | protected function compileOffset(Builder $query, $offset) |
||
443 | { |
||
444 | if (!isset($query->limit)) { |
||
445 | throw new \Exception("You can't set offset without limit for arangodb"); |
||
446 | } |
||
447 | return ''; |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * Wrap collection name |
||
452 | * @param string $collection |
||
453 | * @return string |
||
454 | */ |
||
455 | protected function wrapCollection($collection) |
||
456 | { |
||
457 | return '`'.trim($collection, '`').'`'; |
||
458 | } |
||
459 | |||
460 | protected function getClearColumnName($column) |
||
461 | { |
||
462 | $parts = explode('.', $column); |
||
463 | if (count($parts) > 1) { |
||
464 | $column = $parts[1]; |
||
465 | } |
||
466 | $column = explode('as', $column)[0]; |
||
467 | |||
468 | return trim($column, '` '); |
||
469 | } |
||
470 | |||
471 | protected function getAliasNameFromColumn($column) |
||
472 | { |
||
473 | $parts = explode('as', $column); |
||
474 | if (count($parts) < 2) { |
||
475 | return null; |
||
476 | } |
||
477 | |||
478 | return '`'.trim($parts[1], '` ').'`'; |
||
479 | } |
||
480 | } |
||
481 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: