GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( a11bd1...3fc1f5 )
by Anderson
02:01
created

WhereWalker::walk()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
namespace DoctrineElastic\Query\Walker;
4
5
use Doctrine\DBAL\Query\QueryException;
6
use Doctrine\ORM\Query\AST\ArithmeticExpression;
7
use Doctrine\ORM\Query\AST\BetweenExpression;
8
use Doctrine\ORM\Query\AST\ComparisonExpression;
9
use Doctrine\ORM\Query\AST\ConditionalExpression;
10
use Doctrine\ORM\Query\AST\ConditionalPrimary;
11
use Doctrine\ORM\Query\AST\ConditionalTerm;
12
use Doctrine\ORM\Query\AST\InExpression;
13
use Doctrine\ORM\Query\AST\LikeExpression;
14
use Doctrine\ORM\Query\AST\Literal;
15
use Doctrine\ORM\Query\AST\Node;
16
use Doctrine\ORM\Query\AST\NullComparisonExpression;
17
use Doctrine\ORM\Query\AST\PathExpression;
18
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
19
use Doctrine\ORM\Query\AST\WhereClause;
20
use DoctrineElastic\Elastic\ElasticQuery;
21
use DoctrineElastic\Elastic\SearchParams;
22
use DoctrineElastic\Exception\InvalidOperatorException;
23
use DoctrineElastic\Exception\InvalidParamsException;
24
use DoctrineElastic\Hydrate\AnnotationEntityHydrator;
25
use DoctrineElastic\Mapping\Field;
26
use DoctrineElastic\Mapping\MetaField;
27
use DoctrineElastic\Query\Walker\Helper\WalkerHelper;
28
29
/**
30
 * Walker specialist for Where Clause of Query(builder)
31
 *
32
 * @author Ands
33
 */
34
class WhereWalker {
35
36
    /** @var ElasticQuery */
37
    private $query;
38
39
    /** @var string */
40
    private $className;
41
42
    /** @var WalkerHelper */
43
    private $walkerHelper;
44
45
    /** @var AnnotationEntityHydrator */
46
    private $hydrator;
47
48
    private $fieldAnnotations = [];
49
50
    public function __construct(ElasticQuery $query, $className, WalkerHelper $walkerHelper) {
51
        $this->query = $query;
52
        $this->className = $className;
53
        $this->walkerHelper = $walkerHelper;
54
        $this->hydrator = new AnnotationEntityHydrator();
55
56
        $fieldAnnotations = $this->hydrator->extractSpecAnnotations($className, Field::class);
57
        $metaFieldAnnotations = $this->hydrator->extractSpecAnnotations($className, MetaField::class);
58
59
        $this->fieldAnnotations = array_merge($fieldAnnotations, $metaFieldAnnotations);
60
    }
61
62
    private function fetchTermsAndFactors(Node $node) {
63
        $terms = $factors = [];
64
65
        switch (get_class($node)) {
66
            case ConditionalTerm::class:
67
                /** @var ConditionalTerm $node */
68
                $conditionalFactors = $node->conditionalFactors;
69
                foreach ($conditionalFactors as $conditionalFactor) {
70
                    $factors[] = $this->fetchTermsAndFactors($conditionalFactor);
71
                }
72
                break;
73
            case ConditionalPrimary::class:
74
                /** @var ConditionalPrimary $node */
75
                if ($node->isSimpleConditionalExpression()) {
76
                    $factors[] = $node->simpleConditionalExpression;
77
                } else {
78
                    $termsAndFactors = $this->fetchTermsAndFactors($node->conditionalExpression);
0 ignored issues
show
Bug introduced by
It seems like $node->conditionalExpression can be null; however, fetchTermsAndFactors() 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...
79
                    $terms = $termsAndFactors['terms'];
80
                    $factors = $termsAndFactors['factors'];
81
                }
82
                break;
83
            case ConditionalExpression::class:
84
                /** @var ConditionalExpression $node */
85
                $conditionalTerms = $node->conditionalTerms;
86
                foreach ($conditionalTerms as $conditionalTerm) {
87
                    $terms[] = $this->fetchTermsAndFactors($conditionalTerm);
88
                }
89
                break;
90
            default:
91
                $factors[] = $node;
92
        }
93
94
        return compact('factors', 'terms');
95
    }
96
97
    public function walk(WhereClause $whereClause, SearchParams $searchParams) {
98
        $conditionalExpr = $whereClause->conditionalExpression;
99
100
        $termsAndFactors = ($this->fetchTermsAndFactors($conditionalExpr));
101
102
        $this->walkTermsAndFactors($termsAndFactors, $searchParams);
103
    }
104
105
    private function walkTermsAndFactors(array $termsAndFactors, $searchParams) {
106
        foreach ($termsAndFactors['factors'] as $factor) {
107
            $this->walkANDFactor($factor, $searchParams);
108
        }
109
110
        foreach ($termsAndFactors['terms'] as $term) {
111
            $this->walkORTerm($term, $searchParams);
112
        }
113
    }
114
115 View Code Duplication
    private function walkORTerm($term, SearchParams $parentSearchParams, $statement = false) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
116
        if ($statement && !in_array($statement, ['must', 'should', 'must_not', 'should_not'])) {
117
            throw new InvalidParamsException(sprintf(
118
                "Parameter \$boolOptionInsert must be 'must' or 'should', got '%s'", $statement
119
            ));
120
        }
121
122
        $childSearchParams = clone $parentSearchParams;
123
        $parentBody = $parentSearchParams->getBody();
124
        $childSearchParams->setBody([]);
125
126
        if (is_array($term) && !empty($term)) {
127
            $this->walkTermsAndFactors($term, $childSearchParams);
128
        } else if ($term instanceof Node) {
129
            $this->walkConditionalPrimary($term, $childSearchParams);
130
        }
131
132
        if (!empty($childSearchParams->getBody())) {
133
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, 'should');
134
            $parentSearchParams->setBody($parentBody);
135
        }
136
    }
137
138 View Code Duplication
    private function walkANDFactor($factor, SearchParams $parentSearchParams, $statement = false) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
139
        if ($statement && !in_array($statement, ['must', 'should', 'must_not', 'should_not'])) {
140
            throw new InvalidParamsException(sprintf(
141
                "Parameter \$boolOptionInsert must be 'must' or 'should', got '%s'", $statement
142
            ));
143
        }
144
145
        $childSearchParams = clone $parentSearchParams;
146
        $parentBody = $parentSearchParams->getBody();
147
        $childSearchParams->setBody([]);
148
149
        if (is_array($factor) && !empty($factor)) {
150
            $this->walkTermsAndFactors($factor, $childSearchParams);
151
        } else if ($factor instanceof Node) {
152
            $this->walkConditionalPrimary($factor, $childSearchParams);
153
        }
154
155
        if (!empty($childSearchParams->getBody())) {
156
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, 'must');
157
            $parentSearchParams->setBody($parentBody);
158
        }
159
    }
160
161
    private function walkConditionalPrimary(Node $node, SearchParams $searchParams) {
162
        switch (get_class($node)) {
163
            case ComparisonExpression::class:
164
                /** @var ComparisonExpression $node */
165
                $this->walkComparisonExpression($node, $searchParams);
166
                break;
167
            case LikeExpression::class:
168
                /** @var LikeExpression $node */
169
                $this->walkLikeExpression($node, $searchParams);
170
                break;
171
            case BetweenExpression::class:
172
                /** @var BetweenExpression $node */
173
                $this->walkBetweenExpression($node, $searchParams);
174
                break;
175
            case NullComparisonExpression::class:
176
                /** @var NullComparisonExpression $node */
177
                $this->walkNullComparissionExpression($node, $searchParams);
178
                break;
179
            case InExpression::class:
180
                /** @var InExpression $node */
181
                $this->walkInExpression($node, $searchParams);
182
                break;
183
            default:
184
                throw new InvalidOperatorException(sprintf('%s operation not allowed', get_class($node)));
185
        }
186
    }
187
188
    private function walkInExpression(InExpression $inExpression, SearchParams $searchParams) {
189
        if (!$inExpression->expression->isSimpleArithmeticExpression()) {
190
            throw new QueryException("'IN' Expression is not allowed if not a simple IN query. ");
191
        }
192
193
        $pathExpr = $inExpression->expression->simpleArithmeticExpression;
194
195
        if ($pathExpr instanceof PathExpression) {
196
            $childSearchParams = clone $searchParams;
197
            $childSearchParams->setBody([]);
198
            $parentBody = $searchParams->getBody();
199
            $operator = $inExpression->not ? OperatorsMap::NEQ : OperatorsMap::EQ;
200
201
            /** @var Literal $literal */
202
            foreach ($inExpression->literals as $literal) {
203
                $conditional = new ComparisonExpression($pathExpr, $operator, $literal);
204
205
                $this->walkComparisonExpression($conditional, $childSearchParams);
206
            }
207
208
            $boolOp = $inExpression->not ? 'must' : 'should';
209
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, $boolOp);
210
            $searchParams->setBody($parentBody);
211
        } else {
212
            throw new QueryException("'IN' expression must to point to a valid field path expression. ");
213
        }
214
    }
215
216
    private function walkComparisonExpression(ComparisonExpression $compExpr, SearchParams $searchParams) {
217
        /** @var ArithmeticExpression $leftExpr */
218
        $leftExpr = $compExpr->leftExpression;
219
        /** @var ArithmeticExpression $rightExpr */
220
        $rightExpr = $compExpr->rightExpression;
221
222
        $operator = $compExpr->operator;
223
224
        /** @var PathExpression $pathExpr */
225
        if ($leftExpr instanceof PathExpression) {
226
            $pathExpr = $leftExpr;
227
        } else {
228
            $pathExpr = $leftExpr->simpleArithmeticExpression;
229
        }
230
231
        /** @var Literal|PathExpression $valueExpr */
232
        if ($rightExpr instanceof Literal) {
233
            $valueExpr = $rightExpr;
234
        } else {
235
            $valueExpr = $rightExpr->simpleArithmeticExpression;
236
        }
237
238
        $ESfield = $this->getFieldOrThrowError($pathExpr->field);
0 ignored issues
show
Bug introduced by
The property field does not seem to exist in Doctrine\ORM\Query\AST\SimpleArithmeticExpression.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
239
        $field = $ESfield->name;
240
241
        if ($valueExpr instanceof Literal) {
242
            $value = $valueExpr->value;
243
        } else if ($valueExpr instanceof PathExpression) {
244
            $value = $valueExpr->identificationVariable;
245
        } else {
246
            throw new \Doctrine\ORM\Query\QueryException("Right side expression for '$field' is not suported. ");
247
        }
248
249
        $this->addBodyStatement($field, $operator, $value, $searchParams);
250
    }
251
252
    private function walkLikeExpression(LikeExpression $likeExpr, SearchParams $searchParams) {
253
        $operator = $likeExpr->not ? OperatorsMap::UNLIKE : OperatorsMap::LIKE;
254
255
        /** @var PathExpression $stringExpr */
256
        $stringExpr = $likeExpr->stringExpression;
257
        /** @var Literal $stringPattern */
258
        $stringPattern = $likeExpr->stringPattern;
259
260
        $ESField = $this->getFieldOrThrowError($stringExpr->field);
261
        $field = $ESField->name;
262
        $value = $stringPattern->value;
263
264
        $this->addBodyStatement($field, $operator, $value, $searchParams);
265
    }
266
267
    private function walkBetweenExpression(BetweenExpression $betweenExpr, SearchParams $searchParams) {
268
        $leftRangeExpr = $betweenExpr->leftBetweenExpression;
269
        $rightRangeExpr = $betweenExpr->rightBetweenExpression;
270
        $fieldExpr = $betweenExpr->expression;
271
272
        if ($leftRangeExpr->isSimpleArithmeticExpression()
273
            && $rightRangeExpr->isSimpleArithmeticExpression()
274
            && $fieldExpr->isSimpleArithmeticExpression()
275
        ) {
276
            /** @var Literal $lSimpleArithExpr */
277
            $lSimpleArithExpr = $leftRangeExpr->simpleArithmeticExpression;
278
            /** @var Literal $rSimpleArithExpr */
279
            $rSimpleArithExpr = $rightRangeExpr->simpleArithmeticExpression;
280
            /** @var PathExpression $fieldArithExpr */
281
            $fieldArithExpr = $fieldExpr->simpleArithmeticExpression;
282
283
            $value1 = $lSimpleArithExpr->value;
284
            $value2 = $rSimpleArithExpr->value;
285
286
            $ESField = $this->getFieldOrThrowError($fieldArithExpr->field);
287
            $field = $ESField->name;
288
289
            $this->addBodyStatement($field, OperatorsMap::GTE, $value1, $searchParams);
290
            $this->addBodyStatement($field, OperatorsMap::LTE, $value2, $searchParams);
291
        } else {
292
            throw new InvalidOperatorException(sprintf('Between operation with not simple expression is not allowed. '));
293
        }
294
    }
295
296
    private function walkNullComparissionExpression(NullComparisonExpression $nullComExpr, SearchParams $searchParams) {
297
        /** @var PathExpression $expr */
298
        $expr = $nullComExpr->expression;
299
300
        $ESfield = $this->getFieldOrThrowError($expr->field);
301
        $field = $ESfield->name;
302
        $operator = $nullComExpr->not ? OperatorsMap::NEQ : OperatorsMap::EQ;
303
304
        $this->addBodyStatement($field, $operator, null, $searchParams);
305
    }
306
307
    private function addBodyStatement($field, $operator, $value, SearchParams $searchParams) {
308
        $body = $searchParams->getBody();
309
        $this->walkerHelper->addBodyStatement($field, $operator, $value, $body);
310
        $searchParams->setBody($body);
311
    }
312
313
    /**
314
     * @param string $columnName
315
     * @return Field
316
     * @throws QueryException
317
     */
318
    private function getFieldOrThrowError($columnName) {
319
        if (!isset($this->fieldAnnotations[$columnName])) {
320
            throw new QueryException(sprintf(
321
                "Unrecognized field '%s' in %s entity class. Does this column exist or have a Field Annotation?",
322
                $columnName, $this->className
323
            ));
324
        }
325
326
        return $this->fieldAnnotations[$columnName];
327
    }
328
}