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 | * Doctrine ORM |
||
4 | * |
||
5 | * LICENSE |
||
6 | * |
||
7 | * This source file is subject to the new BSD license that is bundled |
||
8 | * with this package in the file LICENSE.txt. |
||
9 | * If you did not receive a copy of the license and are unable to |
||
10 | * obtain it through the world-wide-web, please send an email |
||
11 | * to [email protected] so I can send you a copy immediately. |
||
12 | */ |
||
13 | |||
14 | namespace Eccube\Doctrine\ORM\Tools\Pagination; |
||
15 | |||
16 | use Doctrine\DBAL\Platforms\DB2Platform; |
||
17 | use Doctrine\DBAL\Platforms\OraclePlatform; |
||
18 | use Doctrine\DBAL\Platforms\PostgreSqlPlatform; |
||
19 | use Doctrine\DBAL\Platforms\SQLAnywherePlatform; |
||
20 | use Doctrine\DBAL\Platforms\SQLServerPlatform; |
||
21 | use Doctrine\ORM\Query\AST\ArithmeticExpression; |
||
22 | use Doctrine\ORM\Query\AST\ArithmeticFactor; |
||
23 | use Doctrine\ORM\Query\AST\ArithmeticTerm; |
||
24 | use Doctrine\ORM\Query\AST\Literal; |
||
25 | use Doctrine\ORM\Query\AST\OrderByClause; |
||
26 | use Doctrine\ORM\Query\AST\OrderByItem; |
||
27 | use Doctrine\ORM\Query\AST\PartialObjectExpression; |
||
28 | use Doctrine\ORM\Query\AST\PathExpression; |
||
29 | use Doctrine\ORM\Query\AST\SelectExpression; |
||
30 | use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; |
||
31 | use Doctrine\ORM\Query\Expr\OrderBy; |
||
32 | use Doctrine\ORM\Query\Expr\Select; |
||
33 | use Doctrine\ORM\Query\SqlWalker; |
||
34 | use Doctrine\ORM\Query\AST\SelectStatement; |
||
35 | |||
36 | /** |
||
37 | * Wraps the query in order to select root entity IDs for pagination. |
||
38 | * |
||
39 | * Given a DQL like `SELECT u FROM User u` it will generate an SQL query like: |
||
40 | * SELECT DISTINCT <id> FROM (<original SQL>) LIMIT x OFFSET y |
||
41 | * |
||
42 | * Works with composite keys but cannot deal with queries that have multiple |
||
43 | * root entities (e.g. `SELECT f, b from Foo, Bar`) |
||
44 | * |
||
45 | * @author Sander Marechal <[email protected]> |
||
46 | */ |
||
47 | class LimitSubqueryOutputWalker extends SqlWalker |
||
48 | { |
||
49 | /** |
||
50 | * @var \Doctrine\DBAL\Platforms\AbstractPlatform |
||
51 | */ |
||
52 | private $platform; |
||
53 | |||
54 | /** |
||
55 | * @var \Doctrine\ORM\Query\ResultSetMapping |
||
56 | */ |
||
57 | private $rsm; |
||
58 | |||
59 | /** |
||
60 | * @var array |
||
61 | */ |
||
62 | private $queryComponents; |
||
63 | |||
64 | /** |
||
65 | * @var int |
||
66 | */ |
||
67 | private $firstResult; |
||
68 | |||
69 | /** |
||
70 | * @var int |
||
71 | */ |
||
72 | private $maxResults; |
||
73 | |||
74 | /** |
||
75 | * @var \Doctrine\ORM\EntityManager |
||
76 | */ |
||
77 | private $em; |
||
78 | |||
79 | /** |
||
80 | * The quote strategy. |
||
81 | * |
||
82 | * @var \Doctrine\ORM\Mapping\QuoteStrategy |
||
83 | */ |
||
84 | private $quoteStrategy; |
||
85 | |||
86 | /** |
||
87 | * @var array |
||
88 | */ |
||
89 | private $orderByPathExpressions = array(); |
||
90 | |||
91 | /** |
||
92 | * @var bool We don't want to add path expressions from sub-selects into the select clause of the containing query. |
||
93 | * This state flag simply keeps track on whether we are walking on a subquery or not |
||
94 | */ |
||
95 | private $inSubSelect = false; |
||
96 | |||
97 | /** |
||
98 | * Constructor. |
||
99 | * |
||
100 | * Stores various parameters that are otherwise unavailable |
||
101 | * because Doctrine\ORM\Query\SqlWalker keeps everything private without |
||
102 | * accessors. |
||
103 | * |
||
104 | * @param \Doctrine\ORM\Query $query |
||
105 | * @param \Doctrine\ORM\Query\ParserResult $parserResult |
||
106 | * @param array $queryComponents |
||
107 | */ |
||
108 | 16 | public function __construct($query, $parserResult, array $queryComponents) |
|
109 | { |
||
110 | 16 | $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); |
|
111 | 16 | $this->rsm = $parserResult->getResultSetMapping(); |
|
112 | 16 | $this->queryComponents = $queryComponents; |
|
113 | |||
114 | // Reset limit and offset |
||
115 | 16 | $this->firstResult = $query->getFirstResult(); |
|
116 | 16 | $this->maxResults = $query->getMaxResults(); |
|
117 | 16 | $query->setFirstResult(null)->setMaxResults(null); |
|
118 | |||
119 | 16 | $this->em = $query->getEntityManager(); |
|
120 | 16 | $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); |
|
121 | |||
122 | 16 | parent::__construct($query, $parserResult, $queryComponents); |
|
123 | } |
||
124 | |||
125 | /** |
||
126 | * Check if the platform supports the ROW_NUMBER window function. |
||
127 | * |
||
128 | * @return bool |
||
129 | */ |
||
130 | 16 | private function platformSupportsRowNumber() |
|
131 | { |
||
132 | 16 | return $this->platform instanceof PostgreSqlPlatform |
|
133 | 16 | || $this->platform instanceof SQLServerPlatform |
|
134 | 16 | || $this->platform instanceof OraclePlatform |
|
135 | 16 | || $this->platform instanceof SQLAnywherePlatform |
|
136 | 16 | || $this->platform instanceof DB2Platform |
|
137 | || (method_exists($this->platform, 'supportsRowNumberFunction') |
||
138 | 16 | && $this->platform->supportsRowNumberFunction()); |
|
0 ignored issues
–
show
|
|||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Rebuilds a select statement's order by clause for use in a |
||
143 | * ROW_NUMBER() OVER() expression. |
||
144 | * |
||
145 | * @param SelectStatement $AST |
||
146 | */ |
||
147 | 16 | private function rebuildOrderByForRowNumber(SelectStatement $AST) |
|
148 | { |
||
149 | 16 | $orderByClause = $AST->orderByClause; |
|
150 | 16 | $selectAliasToExpressionMap = array(); |
|
151 | // Get any aliases that are available for select expressions. |
||
152 | 16 | foreach ($AST->selectClause->selectExpressions as $selectExpression) { |
|
153 | 16 | $selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression; |
|
154 | } |
||
155 | |||
156 | // Rebuild string orderby expressions to use the select expression they're referencing |
||
157 | 16 | foreach ($orderByClause->orderByItems as $orderByItem) { |
|
158 | 16 | if (is_string($orderByItem->expression) && isset($selectAliasToExpressionMap[$orderByItem->expression])) { |
|
159 | 16 | $orderByItem->expression = $selectAliasToExpressionMap[$orderByItem->expression]; |
|
160 | } |
||
161 | } |
||
162 | 16 | $func = new RowNumberOverFunction('dctrn_rownum'); |
|
163 | 16 | $func->orderByClause = $AST->orderByClause; |
|
164 | 16 | $AST->selectClause->selectExpressions[] = new SelectExpression($func, 'dctrn_rownum', true); |
|
165 | |||
166 | // No need for an order by clause, we'll order by rownum in the outer query. |
||
167 | 16 | $AST->orderByClause = null; |
|
168 | } |
||
169 | |||
170 | /** |
||
171 | * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. |
||
172 | * |
||
173 | * @param SelectStatement $AST |
||
174 | * |
||
175 | * @return string |
||
176 | * |
||
177 | * @throws \RuntimeException |
||
178 | */ |
||
179 | 16 | public function walkSelectStatement(SelectStatement $AST) |
|
180 | { |
||
181 | 16 | if ($this->platformSupportsRowNumber()) { |
|
182 | 16 | return $this->walkSelectStatementWithRowNumber($AST); |
|
183 | } |
||
184 | return $this->walkSelectStatementWithoutRowNumber($AST); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. |
||
189 | * This method is for use with platforms which support ROW_NUMBER. |
||
190 | * |
||
191 | * @param SelectStatement $AST |
||
192 | * |
||
193 | * @return string |
||
194 | * |
||
195 | * @throws \RuntimeException |
||
196 | */ |
||
197 | 16 | public function walkSelectStatementWithRowNumber(SelectStatement $AST) |
|
198 | { |
||
199 | 16 | $hasOrderBy = false; |
|
200 | 16 | $outerOrderBy = ' ORDER BY dctrn_minrownum ASC'; |
|
201 | 16 | $orderGroupBy = ''; |
|
202 | 16 | if ($AST->orderByClause instanceof OrderByClause) { |
|
203 | 16 | $hasOrderBy = true; |
|
204 | 16 | $this->rebuildOrderByForRowNumber($AST); |
|
205 | } |
||
206 | |||
207 | 16 | $innerSql = $this->getInnerSQL($AST); |
|
208 | |||
209 | 16 | $sqlIdentifier = $this->getSQLIdentifier($AST); |
|
210 | |||
211 | 16 | if ($hasOrderBy) { |
|
212 | 16 | $orderGroupBy = ' GROUP BY ' . implode(', ', $sqlIdentifier); |
|
213 | 16 | $sqlIdentifier[] = 'MIN(' . $this->walkResultVariable('dctrn_rownum') . ') AS dctrn_minrownum'; |
|
214 | } |
||
215 | |||
216 | // Build the counter query |
||
217 | 16 | $sql = sprintf( |
|
218 | 16 | 'SELECT DISTINCT %s FROM (%s) dctrn_result', |
|
219 | 16 | implode(', ', $sqlIdentifier), |
|
220 | $innerSql |
||
221 | ); |
||
222 | |||
223 | 16 | if ($hasOrderBy) { |
|
224 | 16 | $sql .= $orderGroupBy . $outerOrderBy; |
|
225 | } |
||
226 | |||
227 | // Apply the limit and offset. |
||
228 | 16 | $sql = $this->platform->modifyLimitQuery( |
|
229 | $sql, |
||
230 | 16 | $this->maxResults, |
|
231 | 16 | $this->firstResult |
|
232 | ); |
||
233 | |||
234 | // Add the columns to the ResultSetMapping. It's not really nice but |
||
235 | // it works. Preferably I'd clear the RSM or simply create a new one |
||
236 | // but that is not possible from inside the output walker, so we dirty |
||
237 | // up the one we have. |
||
238 | 16 | foreach ($sqlIdentifier as $property => $alias) { |
|
239 | 16 | $this->rsm->addScalarResult($alias, $property); |
|
240 | } |
||
241 | |||
242 | 16 | return $sql; |
|
243 | } |
||
244 | |||
245 | /** |
||
246 | * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. |
||
247 | * This method is for platforms which DO NOT support ROW_NUMBER. |
||
248 | * |
||
249 | * @param SelectStatement $AST |
||
250 | * @param bool $addMissingItemsFromOrderByToSelect |
||
251 | * |
||
252 | * @return string |
||
253 | * |
||
254 | * @throws \RuntimeException |
||
255 | */ |
||
256 | public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, $addMissingItemsFromOrderByToSelect = true) |
||
257 | { |
||
258 | // We don't want to call this recursively! |
||
259 | if ($AST->orderByClause instanceof OrderByClause && $addMissingItemsFromOrderByToSelect) { |
||
260 | // In the case of ordering a query by columns from joined tables, we |
||
261 | // must add those columns to the select clause of the query BEFORE |
||
262 | // the SQL is generated. |
||
263 | $this->addMissingItemsFromOrderByToSelect($AST); |
||
264 | } |
||
265 | |||
266 | // Remove order by clause from the inner query |
||
267 | // It will be re-appended in the outer select generated by this method |
||
268 | $orderByClause = $AST->orderByClause; |
||
269 | $AST->orderByClause = null; |
||
270 | |||
271 | $innerSql = $this->getInnerSQL($AST); |
||
272 | |||
273 | $sqlIdentifier = $this->getSQLIdentifier($AST); |
||
274 | |||
275 | // Build the counter query |
||
276 | $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', |
||
277 | implode(', ', $sqlIdentifier), $innerSql); |
||
278 | |||
279 | // http://www.doctrine-project.org/jira/browse/DDC-1958 |
||
280 | $sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause); |
||
0 ignored issues
–
show
It seems like
$orderByClause defined by $AST->orderByClause on line 268 can be null ; however, Eccube\Doctrine\ORM\Tool...::preserveSqlOrdering() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
281 | |||
282 | // Apply the limit and offset. |
||
283 | $sql = $this->platform->modifyLimitQuery( |
||
284 | $sql, $this->maxResults, $this->firstResult |
||
285 | ); |
||
286 | |||
287 | // Add the columns to the ResultSetMapping. It's not really nice but |
||
288 | // it works. Preferably I'd clear the RSM or simply create a new one |
||
289 | // but that is not possible from inside the output walker, so we dirty |
||
290 | // up the one we have. |
||
291 | foreach ($sqlIdentifier as $property => $alias) { |
||
292 | $this->rsm->addScalarResult($alias, $property); |
||
293 | } |
||
294 | |||
295 | // Restore orderByClause |
||
296 | $AST->orderByClause = $orderByClause; |
||
297 | |||
298 | return $sql; |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Finds all PathExpressions in an AST's OrderByClause, and ensures that |
||
303 | * the referenced fields are present in the SelectClause of the passed AST. |
||
304 | * |
||
305 | * @param SelectStatement $AST |
||
306 | */ |
||
307 | private function addMissingItemsFromOrderByToSelect(SelectStatement $AST) |
||
308 | { |
||
309 | $this->orderByPathExpressions = array(); |
||
310 | |||
311 | // We need to do this in another walker because otherwise we'll end up |
||
312 | // polluting the state of this one. |
||
313 | $walker = clone $this; |
||
314 | |||
315 | // This will populate $orderByPathExpressions via |
||
316 | // LimitSubqueryOutputWalker::walkPathExpression, which will be called |
||
317 | // as the select statement is walked. We'll end up with an array of all |
||
318 | // path expressions referenced in the query. |
||
319 | $walker->walkSelectStatementWithoutRowNumber($AST, false); |
||
320 | $orderByPathExpressions = $walker->getOrderByPathExpressions(); |
||
321 | |||
322 | // Get a map of referenced identifiers to field names. |
||
323 | $selects = array(); |
||
324 | foreach ($orderByPathExpressions as $pathExpression) { |
||
325 | $idVar = $pathExpression->identificationVariable; |
||
326 | $field = $pathExpression->field; |
||
327 | if (!isset($selects[$idVar])) { |
||
328 | $selects[$idVar] = array(); |
||
329 | } |
||
330 | $selects[$idVar][$field] = true; |
||
331 | } |
||
332 | |||
333 | // Loop the select clause of the AST and exclude items from $select |
||
334 | // that are already being selected in the query. |
||
335 | foreach ($AST->selectClause->selectExpressions as $selectExpression) { |
||
336 | if ($selectExpression instanceof SelectExpression) { |
||
337 | $idVar = $selectExpression->expression; |
||
338 | if (!is_string($idVar)) { |
||
339 | continue; |
||
340 | } |
||
341 | $field = $selectExpression->fieldIdentificationVariable; |
||
342 | if ($field === null) { |
||
343 | // No need to add this select, as we're already fetching the whole object. |
||
344 | unset($selects[$idVar]); |
||
345 | } else { |
||
346 | unset($selects[$idVar][$field]); |
||
347 | } |
||
348 | } |
||
349 | } |
||
350 | |||
351 | // Add select items which were not excluded to the AST's select clause. |
||
352 | foreach ($selects as $idVar => $fields) { |
||
353 | $AST->selectClause->selectExpressions[] = new SelectExpression(new PartialObjectExpression($idVar, array_keys($fields)), null, true); |
||
354 | } |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * Generates new SQL for statements with an order by clause |
||
359 | * |
||
360 | * @param array $sqlIdentifier |
||
361 | * @param string $innerSql |
||
362 | * @param string $sql |
||
363 | * @param OrderByClause $orderByClause |
||
364 | * |
||
365 | * @return string |
||
366 | */ |
||
367 | private function preserveSqlOrdering(array $sqlIdentifier, $innerSql, $sql, $orderByClause) |
||
368 | { |
||
369 | // If the sql statement has an order by clause, we need to wrap it in a new select distinct |
||
370 | // statement |
||
371 | if (! $orderByClause instanceof OrderByClause) { |
||
372 | return $sql; |
||
373 | } |
||
374 | |||
375 | // Rebuild the order by clause to work in the scope of the new select statement |
||
376 | /* @var array $orderBy an array of rebuilt order by items */ |
||
377 | $orderBy = $this->rebuildOrderByClauseForOuterScope($orderByClause); |
||
378 | |||
379 | $innerSqlIdentifier = $sqlIdentifier; |
||
380 | |||
381 | foreach ($orderBy as $field) { |
||
382 | $field = preg_replace('/((\S+)\s+(ASC|DESC)\s*,?)*/', '${2}', $field); |
||
383 | |||
384 | // skip fields that are selected by identifiers, |
||
385 | // if those are ordered by in the query |
||
386 | if (in_array($field, $sqlIdentifier, true)) { |
||
387 | continue; |
||
388 | } |
||
389 | $innerSqlIdentifier[] = $field; |
||
390 | } |
||
391 | |||
392 | // Build the innner select statement |
||
393 | $sql = sprintf( |
||
394 | 'SELECT DISTINCT %s FROM (%s) dctrn_result_inner ORDER BY %s', |
||
395 | implode(', ', $innerSqlIdentifier), |
||
396 | $innerSql, |
||
397 | implode(', ', $orderBy) |
||
398 | ); |
||
399 | |||
400 | // now only select distinct identifier |
||
401 | $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), $sql); |
||
402 | |||
403 | return $sql; |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Generates a new order by clause that works in the scope of a select query wrapping the original |
||
408 | * |
||
409 | * @param OrderByClause $orderByClause |
||
410 | * @return array |
||
411 | */ |
||
412 | private function rebuildOrderByClauseForOuterScope(OrderByClause $orderByClause) |
||
413 | { |
||
414 | $dqlAliasToSqlTableAliasMap |
||
415 | = $searchPatterns |
||
416 | = $replacements |
||
417 | = $dqlAliasToClassMap |
||
418 | = $selectListAdditions |
||
419 | = $orderByItems |
||
420 | = array(); |
||
421 | |||
422 | // Generate DQL alias -> SQL table alias mapping |
||
423 | foreach(array_keys($this->rsm->aliasMap) as $dqlAlias) { |
||
424 | $dqlAliasToClassMap[$dqlAlias] = $class = $this->queryComponents[$dqlAlias]['metadata']; |
||
425 | $dqlAliasToSqlTableAliasMap[$dqlAlias] = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); |
||
426 | } |
||
427 | |||
428 | // Pattern to find table path expressions in the order by clause |
||
429 | $fieldSearchPattern = '/(?<![a-z0-9_])%s\.%s(?![a-z0-9_])/i'; |
||
430 | |||
431 | // Generate search patterns for each field's path expression in the order by clause |
||
432 | foreach($this->rsm->fieldMappings as $fieldAlias => $fieldName) { |
||
433 | $dqlAliasForFieldAlias = $this->rsm->columnOwnerMap[$fieldAlias]; |
||
434 | $class = $dqlAliasToClassMap[$dqlAliasForFieldAlias]; |
||
435 | |||
436 | // If the field is from a joined child table, we won't be ordering |
||
437 | // on it. |
||
438 | if (!isset($class->fieldMappings[$fieldName])) { |
||
439 | continue; |
||
440 | } |
||
441 | |||
442 | $fieldMapping = $class->fieldMappings[$fieldName]; |
||
443 | |||
444 | // Get the proper column name as will appear in the select list |
||
445 | $columnName = $this->quoteStrategy->getColumnName( |
||
446 | $fieldName, |
||
447 | $dqlAliasToClassMap[$dqlAliasForFieldAlias], |
||
448 | $this->em->getConnection()->getDatabasePlatform() |
||
449 | ); |
||
450 | |||
451 | // Get the SQL table alias for the entity and field |
||
452 | $sqlTableAliasForFieldAlias = $dqlAliasToSqlTableAliasMap[$dqlAliasForFieldAlias]; |
||
453 | if (isset($fieldMapping['declared']) && $fieldMapping['declared'] !== $class->name) { |
||
454 | // Field was declared in a parent class, so we need to get the proper SQL table alias |
||
455 | // for the joined parent table. |
||
456 | $otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']); |
||
457 | if (!$otherClassMetadata->isMappedSuperclass) { |
||
458 | $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias); |
||
459 | |||
460 | } |
||
461 | } |
||
462 | |||
463 | // Compose search/replace patterns |
||
464 | $searchPatterns[] = sprintf($fieldSearchPattern, $sqlTableAliasForFieldAlias, $columnName); |
||
465 | $replacements[] = $fieldAlias; |
||
466 | } |
||
467 | |||
468 | foreach($orderByClause->orderByItems as $orderByItem) { |
||
469 | // Walk order by item to get string representation of it |
||
470 | $orderByItemString = $this->walkOrderByItem($orderByItem); |
||
471 | |||
472 | // Replace path expressions in the order by clause with their column alias |
||
473 | $orderByItemString = preg_replace($searchPatterns, $replacements, $orderByItemString); |
||
474 | |||
475 | $orderByItems[] = $orderByItemString; |
||
476 | } |
||
477 | |||
478 | return $orderByItems; |
||
479 | } |
||
480 | |||
481 | /** |
||
482 | * getter for $orderByPathExpressions |
||
483 | * |
||
484 | * @return array |
||
485 | */ |
||
486 | public function getOrderByPathExpressions() |
||
487 | { |
||
488 | return $this->orderByPathExpressions; |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * @param SelectStatement $AST |
||
493 | * |
||
494 | * @return string |
||
495 | * |
||
496 | * @throws \Doctrine\ORM\OptimisticLockException |
||
497 | * @throws \Doctrine\ORM\Query\QueryException |
||
498 | */ |
||
499 | 16 | private function getInnerSQL(SelectStatement $AST) |
|
500 | { |
||
501 | // Set every select expression as visible(hidden = false) to |
||
502 | // make $AST have scalar mappings properly - this is relevant for referencing selected |
||
503 | // fields from outside the subquery, for example in the ORDER BY segment |
||
504 | 16 | $hiddens = array(); |
|
505 | |||
506 | 16 | foreach ($AST->selectClause->selectExpressions as $idx => $expr) { |
|
507 | 16 | $hiddens[$idx] = $expr->hiddenAliasResultVariable; |
|
508 | 16 | $expr->hiddenAliasResultVariable = false; |
|
509 | } |
||
510 | |||
511 | 16 | $innerSql = parent::walkSelectStatement($AST); |
|
0 ignored issues
–
show
It seems like you call parent on a different method (
walkSelectStatement() instead of getInnerSQL() ). Are you sure this is correct? If so, you might want to change this to $this->walkSelectStatement() .
This check looks for a call to a parent method whose name is different than the method from which it is called. Consider the following code: class Daddy
{
protected function getFirstName()
{
return "Eidur";
}
protected function getSurName()
{
return "Gudjohnsen";
}
}
class Son
{
public function getFirstName()
{
return parent::getSurname();
}
}
The
Loading history...
|
|||
512 | |||
513 | // Restore hiddens |
||
514 | 16 | foreach ($AST->selectClause->selectExpressions as $idx => $expr) { |
|
515 | 16 | $expr->hiddenAliasResultVariable = $hiddens[$idx]; |
|
516 | } |
||
517 | |||
518 | 16 | return $innerSql; |
|
519 | } |
||
520 | |||
521 | /** |
||
522 | * @param SelectStatement $AST |
||
523 | * |
||
524 | * @return array |
||
525 | */ |
||
526 | 16 | private function getSQLIdentifier(SelectStatement $AST) |
|
527 | { |
||
528 | // Find out the SQL alias of the identifier column of the root entity. |
||
529 | // It may be possible to make this work with multiple root entities but that |
||
530 | // would probably require issuing multiple queries or doing a UNION SELECT. |
||
531 | // So for now, it's not supported. |
||
532 | |||
533 | // Get the root entity and alias from the AST fromClause. |
||
534 | 16 | $from = $AST->fromClause->identificationVariableDeclarations; |
|
535 | 16 | if (count($from) !== 1) { |
|
536 | throw new \RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); |
||
537 | } |
||
538 | |||
539 | 16 | $fromRoot = reset($from); |
|
540 | 16 | $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; |
|
541 | 16 | $rootClass = $this->queryComponents[$rootAlias]['metadata']; |
|
542 | 16 | $rootIdentifier = $rootClass->identifier; |
|
543 | |||
544 | // For every identifier, find out the SQL alias by combing through the ResultSetMapping |
||
545 | 16 | $sqlIdentifier = array(); |
|
546 | 16 | View Code Duplication | foreach ($rootIdentifier as $property) { |
547 | 16 | if (isset($rootClass->fieldMappings[$property])) { |
|
548 | 16 | foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { |
|
549 | 16 | if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { |
|
550 | 16 | $sqlIdentifier[$property] = $alias; |
|
551 | } |
||
552 | } |
||
553 | } |
||
554 | |||
555 | 16 | if (isset($rootClass->associationMappings[$property])) { |
|
556 | $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; |
||
557 | |||
558 | foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { |
||
559 | if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { |
||
560 | 16 | $sqlIdentifier[$property] = $alias; |
|
561 | } |
||
562 | } |
||
563 | } |
||
564 | } |
||
565 | |||
566 | 16 | if (count($sqlIdentifier) === 0) { |
|
567 | throw new \RuntimeException('The Paginator does not support Queries which only yield ScalarResults.'); |
||
568 | } |
||
569 | |||
570 | 16 | View Code Duplication | if (count($rootIdentifier) != count($sqlIdentifier)) { |
571 | throw new \RuntimeException(sprintf( |
||
572 | 'Not all identifier properties can be found in the ResultSetMapping: %s', |
||
573 | implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) |
||
574 | )); |
||
575 | } |
||
576 | |||
577 | 16 | return $sqlIdentifier; |
|
578 | } |
||
579 | |||
580 | /** |
||
581 | * {@inheritdoc} |
||
582 | */ |
||
583 | 16 | public function walkPathExpression($pathExpr) |
|
584 | { |
||
585 | 16 | if (!$this->inSubSelect && !$this->platformSupportsRowNumber() && !in_array($pathExpr, $this->orderByPathExpressions)) { |
|
586 | $this->orderByPathExpressions[] = $pathExpr; |
||
587 | } |
||
588 | |||
589 | 16 | return parent::walkPathExpression($pathExpr); |
|
590 | } |
||
591 | |||
592 | /** |
||
593 | * {@inheritdoc} |
||
594 | */ |
||
595 | 1 | public function walkSubSelect($subselect) |
|
596 | { |
||
597 | 1 | $this->inSubSelect = true; |
|
598 | |||
599 | 1 | $sql = parent::walkSubselect($subselect); |
|
600 | |||
601 | 1 | $this->inSubSelect = false; |
|
602 | |||
603 | 1 | return $sql; |
|
604 | } |
||
605 | } |
||
606 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.