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 ( c3c4c7...8168ab )
by Anderson
01:41
created

WhereWalker::getElasticFieldNameOrThrowError()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
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\WhereClause;
19
use DoctrineElastic\Elastic\ElasticQuery;
20
use DoctrineElastic\Elastic\SearchParams;
21
use DoctrineElastic\Exception\InvalidOperatorException;
22
use DoctrineElastic\Exception\InvalidParamsException;
23
use DoctrineElastic\Hydrate\AnnotationEntityHydrator;
24
use DoctrineElastic\Mapping\Field;
25
use DoctrineElastic\Mapping\MetaField;
26
use DoctrineElastic\Query\Walker\Helper\WalkerHelper;
27
28
/**
29
 * Walker specialist for Where Clause of Query(builder)
30
 *
31
 * @author Andsalves <[email protected]>
32
 */
33
class WhereWalker {
34
35
    /** @var ElasticQuery */
36
    private $query;
37
38
    /** @var string */
39
    private $className;
40
41
    /** @var WalkerHelper */
42
    private $walkerHelper;
43
44
    /** @var AnnotationEntityHydrator */
45
    private $hydrator;
46
47
    private $fieldAnnotations = [];
48
49
    public function __construct(ElasticQuery $query, $className, WalkerHelper $walkerHelper) {
50
        $this->query = $query;
51
        $this->className = $className;
52
        $this->walkerHelper = $walkerHelper;
53
        $this->hydrator = new AnnotationEntityHydrator();
54
55
        $fieldAnnotations = $this->hydrator->extractSpecAnnotations($className, Field::class);
56
        $metaFieldAnnotations = $this->hydrator->extractSpecAnnotations($className, MetaField::class);
57
58
        $this->fieldAnnotations = array_merge($fieldAnnotations, $metaFieldAnnotations);
59
    }
60
61
    private function fetchTermsAndFactors(Node $node) {
62
        $terms = $factors = [];
63
64
        switch (get_class($node)) {
65
            case ConditionalTerm::class:
66
                /** @var ConditionalTerm $node */
67
                $conditionalFactors = $node->conditionalFactors;
68
                foreach ($conditionalFactors as $conditionalFactor) {
69
                    $factors[] = $this->fetchTermsAndFactors($conditionalFactor);
70
                }
71
                break;
72
            case ConditionalPrimary::class:
73
                /** @var ConditionalPrimary $node */
74
                if ($node->isSimpleConditionalExpression()) {
75
                    $factors[] = $node->simpleConditionalExpression;
76
                } else {
77
                    $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...
78
                    $terms = $termsAndFactors['terms'];
79
                    $factors = $termsAndFactors['factors'];
80
                }
81
                break;
82
            case ConditionalExpression::class:
83
                /** @var ConditionalExpression $node */
84
                $conditionalTerms = $node->conditionalTerms;
85
                foreach ($conditionalTerms as $conditionalTerm) {
86
                    $terms[] = $this->fetchTermsAndFactors($conditionalTerm);
87
                }
88
                break;
89
            default:
90
                $factors[] = $node;
91
        }
92
93
        return compact('factors', 'terms');
94
    }
95
96
    public function walk(WhereClause $whereClause, SearchParams $searchParams) {
97
        $conditionalExpr = $whereClause->conditionalExpression;
98
99
        $termsAndFactors = ($this->fetchTermsAndFactors($conditionalExpr));
100
101
        $this->walkTermsAndFactors($termsAndFactors, $searchParams);
102
    }
103
104
    private function walkTermsAndFactors(array $termsAndFactors, $searchParams) {
105
        foreach ($termsAndFactors['factors'] as $factor) {
106
            $this->walkANDFactor($factor, $searchParams);
107
        }
108
109
        foreach ($termsAndFactors['terms'] as $term) {
110
            $this->walkORTerm($term, $searchParams);
111
        }
112
    }
113
114 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...
115
        if ($statement && !in_array($statement, ['must', 'should', 'must_not', 'should_not'])) {
116
            throw new InvalidParamsException(sprintf(
117
                "Parameter \$boolOptionInsert must be 'must' or 'should', got '%s'", $statement
118
            ));
119
        }
120
121
        $childSearchParams = clone $parentSearchParams;
122
        $parentBody = $parentSearchParams->getBody();
123
        $childSearchParams->setBody([]);
124
125
        if (is_array($term) && !empty($term)) {
126
            $this->walkTermsAndFactors($term, $childSearchParams);
127
        } else if ($term instanceof Node) {
128
            $this->walkConditionalPrimary($term, $childSearchParams);
129
        }
130
131
        if (!empty($childSearchParams->getBody())) {
132
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, 'should');
133
            $parentSearchParams->setBody($parentBody);
134
        }
135
    }
136
137 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...
138
        if ($statement && !in_array($statement, ['must', 'should', 'must_not', 'should_not'])) {
139
            throw new InvalidParamsException(sprintf(
140
                "Parameter \$boolOptionInsert must be 'must' or 'should', got '%s'", $statement
141
            ));
142
        }
143
144
        $childSearchParams = clone $parentSearchParams;
145
        $parentBody = $parentSearchParams->getBody();
146
        $childSearchParams->setBody([]);
147
148
        if (is_array($factor) && !empty($factor)) {
149
            $this->walkTermsAndFactors($factor, $childSearchParams);
150
        } else if ($factor instanceof Node) {
151
            $this->walkConditionalPrimary($factor, $childSearchParams);
152
        }
153
154
        if (!empty($childSearchParams->getBody())) {
155
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, 'must');
156
            $parentSearchParams->setBody($parentBody);
157
        }
158
    }
159
160
    private function walkConditionalPrimary(Node $node, SearchParams $searchParams) {
161
        switch (get_class($node)) {
162
            case ComparisonExpression::class:
163
                /** @var ComparisonExpression $node */
164
                $this->walkComparisonExpression($node, $searchParams);
165
                break;
166
            case LikeExpression::class:
167
                /** @var LikeExpression $node */
168
                $this->walkLikeExpression($node, $searchParams);
169
                break;
170
            case BetweenExpression::class:
171
                /** @var BetweenExpression $node */
172
                $this->walkBetweenExpression($node, $searchParams);
173
                break;
174
            case NullComparisonExpression::class:
175
                /** @var NullComparisonExpression $node */
176
                $this->walkNullComparissionExpression($node, $searchParams);
177
                break;
178
            case InExpression::class:
179
                /** @var InExpression $node */
180
                $this->walkInExpression($node, $searchParams);
181
                break;
182
            default:
183
                throw new InvalidOperatorException(sprintf('%s operation not allowed', get_class($node)));
184
        }
185
    }
186
187
    private function walkInExpression(InExpression $inExpression, SearchParams $searchParams) {
188
        if (!$inExpression->expression->isSimpleArithmeticExpression()) {
189
            throw new QueryException("'IN' Expression is not allowed if not a simple IN query. ");
190
        }
191
192
        $pathExpr = $inExpression->expression->simpleArithmeticExpression;
193
194
        if ($pathExpr instanceof PathExpression) {
195
            $childSearchParams = clone $searchParams;
196
            $childSearchParams->setBody([]);
197
            $parentBody = $searchParams->getBody();
198
            $operator = $inExpression->not ? OperatorsMap::NEQ : OperatorsMap::EQ;
199
200
            /** @var Literal $literal */
201
            foreach ($inExpression->literals as $literal) {
202
                $conditional = new ComparisonExpression($pathExpr, $operator, $literal);
203
204
                $this->walkComparisonExpression($conditional, $childSearchParams);
205
            }
206
207
            $boolOp = $inExpression->not ? 'must' : 'should';
208
            $this->walkerHelper->addSubQueryStatement($childSearchParams->getBody(), $parentBody, $boolOp);
209
            $searchParams->setBody($parentBody);
210
        } else {
211
            throw new QueryException("'IN' expression must to point to a valid field path expression. ");
212
        }
213
    }
214
215
    private function walkComparisonExpression(ComparisonExpression $compExpr, SearchParams $searchParams) {
216
        /** @var ArithmeticExpression $leftExpr */
217
        $leftExpr = $compExpr->leftExpression;
218
        /** @var ArithmeticExpression $rightExpr */
219
        $rightExpr = $compExpr->rightExpression;
220
221
        $operator = $compExpr->operator;
222
223
        /** @var PathExpression $pathExpr */
224
        if ($leftExpr instanceof PathExpression) {
225
            $pathExpr = $leftExpr;
226
        } else {
227
            $pathExpr = $leftExpr->simpleArithmeticExpression;
228
        }
229
230
        /** @var Literal|PathExpression $valueExpr */
231
        if ($rightExpr instanceof Literal) {
232
            $valueExpr = $rightExpr;
233
        } else {
234
            $valueExpr = $rightExpr->simpleArithmeticExpression;
235
        }
236
237
        $field = $this->getElasticFieldNameOrThrowError($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...
238
239
        if ($valueExpr instanceof Literal) {
240
            $value = $valueExpr->value;
241
        } else if ($valueExpr instanceof PathExpression) {
242
            $value = $valueExpr->identificationVariable;
243
        } else {
244
            throw new \Doctrine\ORM\Query\QueryException("Right side expression for '$field' is not suported. ");
245
        }
246
247
        $this->addBodyStatement($field, $operator, $value, $searchParams);
248
    }
249
250
    private function walkLikeExpression(LikeExpression $likeExpr, SearchParams $searchParams) {
251
        $operator = $likeExpr->not ? OperatorsMap::UNLIKE : OperatorsMap::LIKE;
252
253
        /** @var PathExpression $stringExpr */
254
        $stringExpr = $likeExpr->stringExpression;
255
        /** @var Literal $stringPattern */
256
        $stringPattern = $likeExpr->stringPattern;
257
258
        $field = $this->getElasticFieldNameOrThrowError($stringExpr->field);
259
        $value = $stringPattern->value;
260
261
        $this->addBodyStatement($field, $operator, $value, $searchParams);
262
    }
263
264
    private function walkBetweenExpression(BetweenExpression $betweenExpr, SearchParams $searchParams) {
265
        $leftRangeExpr = $betweenExpr->leftBetweenExpression;
266
        $rightRangeExpr = $betweenExpr->rightBetweenExpression;
267
        $fieldExpr = $betweenExpr->expression;
268
269
        if ($leftRangeExpr->isSimpleArithmeticExpression()
270
            && $rightRangeExpr->isSimpleArithmeticExpression()
271
            && $fieldExpr->isSimpleArithmeticExpression()
272
        ) {
273
            /** @var Literal $lSimpleArithExpr */
274
            $lSimpleArithExpr = $leftRangeExpr->simpleArithmeticExpression;
275
            /** @var Literal $rSimpleArithExpr */
276
            $rSimpleArithExpr = $rightRangeExpr->simpleArithmeticExpression;
277
            /** @var PathExpression $fieldArithExpr */
278
            $fieldArithExpr = $fieldExpr->simpleArithmeticExpression;
279
280
            $value1 = $lSimpleArithExpr->value;
281
            $value2 = $rSimpleArithExpr->value;
282
283
            $field = $this->getElasticFieldNameOrThrowError($fieldArithExpr->field);
284
285
            $this->addBodyStatement($field, OperatorsMap::GTE, $value1, $searchParams);
286
            $this->addBodyStatement($field, OperatorsMap::LTE, $value2, $searchParams);
287
        } else {
288
            throw new InvalidOperatorException(sprintf('Between operation with not simple expression is not allowed. '));
289
        }
290
    }
291
292
    private function walkNullComparissionExpression(NullComparisonExpression $nullComExpr, SearchParams $searchParams) {
293
        /** @var PathExpression $expr */
294
        $expr = $nullComExpr->expression;
295
296
        $field = $this->getElasticFieldNameOrThrowError($expr->field);
297
        $operator = $nullComExpr->not ? OperatorsMap::NEQ : OperatorsMap::EQ;
298
299
        $this->addBodyStatement($field, $operator, null, $searchParams);
300
    }
301
302
    private function addBodyStatement($field, $operator, $value, SearchParams $searchParams) {
303
        $body = $searchParams->getBody();
304
        $this->walkerHelper->addBodyStatement($field, $operator, $value, $body);
305
        $searchParams->setBody($body);
306
    }
307
308
    private function getElasticFieldNameOrThrowError($columnName) {
309
        if (strstr($columnName, '.')) {
310
            $fieldLayers = explode('.', $columnName);
311
            $ESfield = $this->getFieldOrThrowError($fieldLayers[0]);
312
            $field = "{$ESfield->name}." . implode('.', array_slice($fieldLayers, 1));
313
        } else {
314
            $ESfield = $this->getFieldOrThrowError($columnName);
315
            $field = $ESfield->name;
316
        }
317
318
        return $field;
319
    }
320
321
    /**
322
     * @param string $columnName
323
     * @return Field
324
     * @throws QueryException
325
     */
326
    private function getFieldOrThrowError($columnName) {
327
        if (!isset($this->fieldAnnotations[$columnName])) {
328
            throw new QueryException(sprintf(
329
                "Unrecognized field '%s' in %s entity class. Does this column exist or have a Field Annotation?",
330
                $columnName, $this->className
331
            ));
332
        }
333
334
        return $this->fieldAnnotations[$columnName];
335
    }
336
}