Expr   F
last analyzed

Complexity

Total Complexity 139

Size/Duplication

Total Lines 1354
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 86.48%

Importance

Changes 0
Metric Value
wmc 139
lcom 1
cbo 5
dl 0
loc 1354
ccs 307
cts 355
cp 0.8648
rs 0.8
c 0
b 0
f 0

75 Methods

Rating   Name   Duplication   Size   Complexity  
A setClassMetadata() 0 4 1
A setOnInsert() 0 7 1
A setQuery() 0 5 1
A size() 0 4 1
A slice() 0 4 1
A sort() 0 8 2
A text() 0 5 1
A type() 0 4 1
A unsetField() 0 6 1
A where() 0 5 1
A __construct() 0 4 1
A addAnd() 0 18 3
A addNor() 0 15 3
A addOr() 0 15 3
A addToSet() 0 10 2
A all() 0 4 1
A bit() 0 6 1
A bitAnd() 0 4 1
A bitOr() 0 4 1
A bitsAllClear() 0 5 1
A bitsAllSet() 0 5 1
A bitsAnyClear() 0 5 1
A bitsAnySet() 0 5 1
A bitXor() 0 4 1
A caseSensitive() 0 15 4
A comment() 0 5 1
A currentDate() 0 10 2
A diacriticSensitive() 0 15 4
A each() 0 4 1
A elemMatch() 0 4 2
A equals() 0 9 2
A exists() 0 4 1
A field() 0 5 1
A geoIntersects() 0 8 2
A geoWithin() 0 8 2
A geoWithinBox() 0 6 1
A geoWithinCenter() 0 6 1
A geoWithinCenterSphere() 0 6 1
A geoWithinPolygon() 0 6 1
A getCurrentField() 0 4 1
A getNewObj() 0 6 1
A getQuery() 0 6 1
A gt() 0 4 1
A gte() 0 4 1
A in() 0 4 1
A inc() 0 6 1
B includesReferenceTo() 0 41 8
A language() 0 10 2
A lt() 0 4 1
A lte() 0 4 1
A max() 0 6 1
A min() 0 6 1
A mod() 0 4 1
A mul() 0 6 1
A near() 0 12 3
A nearSphere() 0 12 3
A not() 0 4 2
A notEqual() 0 4 1
A notIn() 0 4 1
A operator() 0 11 2
A popFirst() 0 6 1
A popLast() 0 6 1
A position() 0 4 1
A pull() 0 10 2
A pullAll() 0 6 1
A push() 0 13 2
A range() 0 4 1
B references() 0 41 8
A rename() 0 6 1
A set() 0 24 4
B getReferenceMapping() 0 32 9
A normalizeSortOrder() 0 8 3
A requiresCurrentField() 0 6 2
B wrapEqualityCriteria() 0 30 9
A setNewObj() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like Expr often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Expr, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Query;
6
7
use BadMethodCallException;
8
use Doctrine\ODM\MongoDB\DocumentManager;
9
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
10
use Doctrine\ODM\MongoDB\Mapping\MappingException;
11
use GeoJson\Geometry\Geometry;
12
use GeoJson\Geometry\Point;
13
use InvalidArgumentException;
14
use LogicException;
15
use MongoDB\BSON\Binary;
16
use MongoDB\BSON\Javascript;
17
use function array_key_exists;
18
use function array_map;
19
use function array_merge;
20
use function array_values;
21
use function assert;
22
use function explode;
23
use function func_get_args;
24
use function in_array;
25
use function is_array;
26
use function is_string;
27
use function key;
28
use function sprintf;
29
use function strpos;
30
use function strtolower;
31
32
/**
33
 * Query expression builder for ODM.
34
 */
35
class Expr
36
{
37
    /**
38
     * The query criteria array.
39
     *
40
     * @var array
41
     */
42
    private $query = [];
43
44
    /**
45
     * The "new object" array containing either a full document or a number of
46
     * atomic update operators.
47
     *
48
     * @see docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter
49
     *
50
     * @var array
51
     */
52
    private $newObj = [];
53
54
    /**
55
     * The current field we are operating on.
56
     *
57
     * @var string|null
58
     */
59
    private $currentField;
60
61
    /**
62
     * The DocumentManager instance for this query
63
     *
64
     * @var DocumentManager
65
     */
66
    private $dm;
67
68
    /**
69
     * The ClassMetadata instance for the document being queried
70
     *
71
     * @var ClassMetadata
72
     */
73
    private $class;
74
75 388
    public function __construct(DocumentManager $dm)
76
    {
77 388
        $this->dm = $dm;
78 388
    }
79
80
    /**
81
     * Add one or more $and clauses to the current query.
82
     *
83
     * @see Builder::addAnd()
84
     * @see http://docs.mongodb.org/manual/reference/operator/and/
85
     *
86
     * @param array|Expr $expression
87
     * @param array|Expr ...$expressions
88
     */
89 3
    public function addAnd($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by Andreas Braun
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Andreas Braun
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
90
    {
91 3
        if (! isset($this->query['$and'])) {
92 3
            $this->query['$and'] = [];
93
        }
94
95 3
        $this->query['$and'] = array_merge(
96 3
            $this->query['$and'],
97 3
            array_map(
98
                static function ($expression) {
99 3
                    return $expression instanceof Expr ? $expression->getQuery() : $expression;
100 3
                },
101 3
                func_get_args()
102
            )
103
        );
104
105 3
        return $this;
106
    }
107
108
    /**
109
     * Add one or more $nor clauses to the current query.
110
     *
111
     * @see Builder::addNor()
112
     * @see http://docs.mongodb.org/manual/reference/operator/nor/
113
     *
114
     * @param array|Expr $expression
115
     * @param array|Expr ...$expressions
116
     */
117 1
    public function addNor($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by Catalin Ciobanu
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Catalin Ciobanu
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
118
    {
119 1
        if (! isset($this->query['$nor'])) {
120 1
            $this->query['$nor'] = [];
121
        }
122
123 1
        $this->query['$nor'] = array_merge(
124 1
            $this->query['$nor'],
125
            array_map(static function ($expression) {
126 1
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
127 1
            }, func_get_args())
128
        );
129
130 1
        return $this;
131
    }
132
133
    /**
134
     * Add one or more $or clauses to the current query.
135
     *
136
     * @see Builder::addOr()
137
     * @see http://docs.mongodb.org/manual/reference/operator/or/
138
     *
139
     * @param array|Expr $expression
140
     * @param array|Expr ...$expressions
141
     */
142 5
    public function addOr($expression, ...$expressions) : self
0 ignored issues
show
Unused Code introduced by Andreas Braun
The parameter $expression is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Andreas Braun
The parameter $expressions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
143
    {
144 5
        if (! isset($this->query['$or'])) {
145 5
            $this->query['$or'] = [];
146
        }
147
148 5
        $this->query['$or'] = array_merge(
149 5
            $this->query['$or'],
150
            array_map(static function ($expression) {
151 5
                return $expression instanceof Expr ? $expression->getQuery() : $expression;
152 5
            }, func_get_args())
153
        );
154
155 5
        return $this;
156
    }
157
158
    /**
159
     * Append one or more values to the current array field only if they do not
160
     * already exist in the array.
161
     *
162
     * If the field does not exist, it will be set to an array containing the
163
     * unique value(s) in the argument. If the field is not an array, the query
164
     * will yield an error.
165
     *
166
     * Multiple values may be specified by provided an Expr object and using
167
     * {@link Expr::each()}.
168
     *
169
     * @see Builder::addToSet()
170
     * @see http://docs.mongodb.org/manual/reference/operator/addToSet/
171
     * @see http://docs.mongodb.org/manual/reference/operator/each/
172
     *
173
     * @param mixed|Expr $valueOrExpression
174
     */
175 5
    public function addToSet($valueOrExpression) : self
176
    {
177 5
        if ($valueOrExpression instanceof Expr) {
178 1
            $valueOrExpression = $valueOrExpression->getQuery();
179
        }
180
181 5
        $this->requiresCurrentField();
182 5
        $this->newObj['$addToSet'][$this->currentField] = $valueOrExpression;
183 5
        return $this;
184
    }
185
186
    /**
187
     * Specify $all criteria for the current field.
188
     *
189
     * @see Builder::all()
190
     * @see http://docs.mongodb.org/manual/reference/operator/all/
191
     */
192 2
    public function all(array $values) : self
193
    {
194 2
        return $this->operator('$all', $values);
195
    }
196
197
    /**
198
     * Apply a bitwise operation on the current field
199
     *
200
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
201
     */
202 3
    protected function bit(string $operator, int $value) : self
203
    {
204 3
        $this->requiresCurrentField();
205 3
        $this->newObj['$bit'][$this->currentField][$operator] = $value;
206 3
        return $this;
207
    }
208
209
    /**
210
     * Apply a bitwise and operation on the current field.
211
     *
212
     * @see Builder::bitAnd()
213
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
214
     */
215 1
    public function bitAnd(int $value) : self
216
    {
217 1
        return $this->bit('and', $value);
218
    }
219
220
    /**
221
     * Apply a bitwise or operation on the current field.
222
     *
223
     * @see Builder::bitOr()
224
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
225
     */
226 1
    public function bitOr(int $value) : self
227
    {
228 1
        return $this->bit('or', $value);
229
    }
230
231
    /**
232
     * Matches documents where all of the bit positions given by the query are
233
     * clear.
234
     *
235
     * @see Builder::bitsAllClear()
236
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllClear/
237
     *
238
     * @param int|array|Binary $value
239
     */
240
    public function bitsAllClear($value) : self
241
    {
242
        $this->requiresCurrentField();
243
        return $this->operator('$bitsAllClear', $value);
244
    }
245
246
    /**
247
     * Matches documents where all of the bit positions given by the query are
248
     * set.
249
     *
250
     * @see Builder::bitsAllSet()
251
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAllSet/
252
     *
253
     * @param int|array|Binary $value
254
     */
255
    public function bitsAllSet($value) : self
256
    {
257
        $this->requiresCurrentField();
258
        return $this->operator('$bitsAllSet', $value);
259
    }
260
261
    /**
262
     * Matches documents where any of the bit positions given by the query are
263
     * clear.
264
     *
265
     * @see Builder::bitsAnyClear()
266
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnyClear/
267
     *
268
     * @param int|array|Binary $value
269
     */
270
    public function bitsAnyClear($value) : self
271
    {
272
        $this->requiresCurrentField();
273
        return $this->operator('$bitsAnyClear', $value);
274
    }
275
276
    /**
277
     * Matches documents where any of the bit positions given by the query are
278
     * set.
279
     *
280
     * @see Builder::bitsAnySet()
281
     * @see https://docs.mongodb.org/manual/reference/operator/query/bitsAnySet/
282
     *
283
     * @param int|array|Binary $value
284
     */
285
    public function bitsAnySet($value) : self
286
    {
287
        $this->requiresCurrentField();
288
        return $this->operator('$bitsAnySet', $value);
289
    }
290
291
    /**
292
     * Apply a bitwise xor operation on the current field.
293
     *
294
     * @see Builder::bitXor()
295
     * @see http://docs.mongodb.org/manual/reference/operator/update/bit/
296
     */
297 1
    public function bitXor(int $value) : self
298
    {
299 1
        return $this->bit('xor', $value);
300
    }
301
302
    /**
303
     * A boolean flag to enable or disable case sensitive search for $text
304
     * criteria.
305
     *
306
     * This method must be called after text().
307
     *
308
     * @see Builder::caseSensitive()
309
     * @see http://docs.mongodb.org/manual/reference/operator/text/
310
     *
311
     * @throws BadMethodCallException If the query does not already have $text criteria.
312
     */
313 3
    public function caseSensitive(bool $caseSensitive) : self
314
    {
315 3
        if (! isset($this->query['$text'])) {
316 1
            throw new BadMethodCallException('This method requires a $text operator (call text() first)');
317
        }
318
319
        // Remove caseSensitive option to keep support for older database versions
320 2
        if ($caseSensitive) {
321 2
            $this->query['$text']['$caseSensitive'] = true;
322 1
        } elseif (isset($this->query['$text']['$caseSensitive'])) {
323 1
            unset($this->query['$text']['$caseSensitive']);
324
        }
325
326 2
        return $this;
327
    }
328
329
    /**
330
     * Associates a comment to any expression taking a query predicate.
331
     *
332
     * @see Builder::comment()
333
     * @see http://docs.mongodb.org/manual/reference/operator/query/comment/
334
     */
335
    public function comment(string $comment) : self
336
    {
337
        $this->query['$comment'] = $comment;
338
        return $this;
339
    }
340
341
    /**
342
     * Sets the value of the current field to the current date, either as a date or a timestamp.
343
     *
344
     * @see Builder::currentDate()
345
     * @see http://docs.mongodb.org/manual/reference/operator/update/currentDate/
346
     *
347
     * @throws InvalidArgumentException If an invalid type is given.
348
     */
349 3
    public function currentDate(string $type = 'date') : self
350
    {
351 3
        if (! in_array($type, ['date', 'timestamp'])) {
352 1
            throw new InvalidArgumentException('Type for currentDate operator must be date or timestamp.');
353
        }
354
355 2
        $this->requiresCurrentField();
356 2
        $this->newObj['$currentDate'][$this->currentField]['$type'] = $type;
357 2
        return $this;
358
    }
359
360
    /**
361
     * A boolean flag to enable or disable diacritic sensitive search for $text
362
     * criteria.
363
     *
364
     * This method must be called after text().
365
     *
366
     * @see Builder::diacriticSensitive()
367
     * @see http://docs.mongodb.org/manual/reference/operator/text/
368
     *
369
     * @throws BadMethodCallException If the query does not already have $text criteria.
370
     */
371 3
    public function diacriticSensitive(bool $diacriticSensitive) : self
372
    {
373 3
        if (! isset($this->query['$text'])) {
374 1
            throw new BadMethodCallException('This method requires a $text operator (call text() first)');
375
        }
376
377
        // Remove diacriticSensitive option to keep support for older database versions
378 2
        if ($diacriticSensitive) {
379 2
            $this->query['$text']['$diacriticSensitive'] = true;
380 1
        } elseif (isset($this->query['$text']['$diacriticSensitive'])) {
381 1
            unset($this->query['$text']['$diacriticSensitive']);
382
        }
383
384 2
        return $this;
385
    }
386
387
    /**
388
     * Add $each criteria to the expression for a $push operation.
389
     *
390
     * @see Expr::push()
391
     * @see http://docs.mongodb.org/manual/reference/operator/each/
392
     */
393 4
    public function each(array $values) : self
394
    {
395 4
        return $this->operator('$each', $values);
396
    }
397
398
    /**
399
     * Specify $elemMatch criteria for the current field.
400
     *
401
     * @see Builder::elemMatch()
402
     * @see http://docs.mongodb.org/manual/reference/operator/elemMatch/
403
     *
404
     * @param array|Expr $expression
405
     */
406 4
    public function elemMatch($expression) : self
407
    {
408 4
        return $this->operator('$elemMatch', $expression instanceof Expr ? $expression->getQuery() : $expression);
409
    }
410
411
    /**
412
     * Specify an equality match for the current field.
413
     *
414
     * @see Builder::equals()
415
     *
416
     * @param mixed $value
417
     */
418 104
    public function equals($value) : self
419
    {
420 104
        if ($this->currentField) {
0 ignored issues
show
Bug Best Practice introduced by Andreas Braun
The expression $this->currentField of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch 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:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
421 103
            $this->query[$this->currentField] = $value;
422
        } else {
423 1
            $this->query = $value;
0 ignored issues
show
Documentation Bug introduced by Andreas Braun
It seems like $value of type * is incompatible with the declared type array of property $query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
424
        }
425 104
        return $this;
426
    }
427
428
    /**
429
     * Specify $exists criteria for the current field.
430
     *
431
     * @see Builder::exists()
432
     * @see http://docs.mongodb.org/manual/reference/operator/exists/
433
     */
434 5
    public function exists(bool $bool) : self
435
    {
436 5
        return $this->operator('$exists', $bool);
437
    }
438
439
    /**
440
     * Set the current field for building the expression.
441
     *
442
     * @see Builder::field()
443
     */
444 192
    public function field(string $field) : self
445
    {
446 192
        $this->currentField = $field;
447 192
        return $this;
448
    }
449
450
    /**
451
     * Add $geoIntersects criteria with a GeoJSON geometry to the expression.
452
     *
453
     * The geometry parameter GeoJSON object or an array corresponding to the
454
     * geometry's JSON representation.
455
     *
456
     * @see Builder::geoIntersects()
457
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
458
     *
459
     * @param array|Geometry $geometry
460
     */
461 2
    public function geoIntersects($geometry) : self
462
    {
463 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by Andreas Braun
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
464 1
            $geometry = $geometry->jsonSerialize();
465
        }
466
467 2
        return $this->operator('$geoIntersects', ['$geometry' => $geometry]);
468
    }
469
470
    /**
471
     * Add $geoWithin criteria with a GeoJSON geometry to the expression.
472
     *
473
     * The geometry parameter GeoJSON object or an array corresponding to the
474
     * geometry's JSON representation.
475
     *
476
     * @see Builder::geoWithin()
477
     * @see http://docs.mongodb.org/manual/reference/operator/geoIntersects/
478
     *
479
     * @param array|Geometry $geometry
480
     */
481 2
    public function geoWithin($geometry) : self
482
    {
483 2
        if ($geometry instanceof Geometry) {
0 ignored issues
show
Bug introduced by Andreas Braun
The class GeoJson\Geometry\Geometry does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
484 1
            $geometry = $geometry->jsonSerialize();
485
        }
486
487 2
        return $this->operator('$geoWithin', ['$geometry' => $geometry]);
488
    }
489
490
    /**
491
     * Add $geoWithin criteria with a $box shape to the expression.
492
     *
493
     * A rectangular polygon will be constructed from a pair of coordinates
494
     * corresponding to the bottom left and top right corners.
495
     *
496
     * Note: the $box operator only supports legacy coordinate pairs and 2d
497
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
498
     *
499
     * @see Builder::geoWithinBox()
500
     * @see http://docs.mongodb.org/manual/reference/operator/box/
501
     */
502 1
    public function geoWithinBox(float $x1, float $y1, float $x2, float $y2) : self
503
    {
504 1
        $shape = ['$box' => [[$x1, $y1], [$x2, $y2]]];
505
506 1
        return $this->operator('$geoWithin', $shape);
507
    }
508
509
    /**
510
     * Add $geoWithin criteria with a $center shape to the expression.
511
     *
512
     * Note: the $center operator only supports legacy coordinate pairs and 2d
513
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
514
     *
515
     * @see Builider::geoWithinCenter()
516
     * @see http://docs.mongodb.org/manual/reference/operator/center/
517
     */
518 1
    public function geoWithinCenter(float $x, float $y, float $radius) : self
519
    {
520 1
        $shape = ['$center' => [[$x, $y], $radius]];
521
522 1
        return $this->operator('$geoWithin', $shape);
523
    }
524
525
    /**
526
     * Add $geoWithin criteria with a $centerSphere shape to the expression.
527
     *
528
     * Note: the $centerSphere operator supports both 2d and 2dsphere indexes.
529
     *
530
     * @see Builder::geoWithinCenterSphere()
531
     * @see http://docs.mongodb.org/manual/reference/operator/centerSphere/
532
     */
533 1
    public function geoWithinCenterSphere(float $x, float $y, float $radius) : self
534
    {
535 1
        $shape = ['$centerSphere' => [[$x, $y], $radius]];
536
537 1
        return $this->operator('$geoWithin', $shape);
538
    }
539
540
    /**
541
     * Add $geoWithin criteria with a $polygon shape to the expression.
542
     *
543
     * Point coordinates are in x, y order (easting, northing for projected
544
     * coordinates, longitude, latitude for geographic coordinates).
545
     *
546
     * The last point coordinate is implicitly connected with the first.
547
     *
548
     * Note: the $polygon operator only supports legacy coordinate pairs and 2d
549
     * indexes. This cannot be used with 2dsphere indexes and GeoJSON shapes.
550
     *
551
     * @see Builder::geoWithinPolygon()
552
     * @see http://docs.mongodb.org/manual/reference/operator/polygon/
553
     *
554
     * @param array $point1    First point of the polygon
555
     * @param array $point2    Second point of the polygon
556
     * @param array $point3    Third point of the polygon
557
     * @param array ...$points Additional points of the polygon
558
     *
559
     * @throws InvalidArgumentException If less than three points are given.
560
     */
561 1
    public function geoWithinPolygon($point1, $point2, $point3, ...$points) : self
0 ignored issues
show
Unused Code introduced by Andreas Braun
The parameter $point1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Andreas Braun
The parameter $point2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Andreas Braun
The parameter $point3 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Andreas Braun
The parameter $points is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
562
    {
563 1
        $shape = ['$polygon' => func_get_args()];
564
565 1
        return $this->operator('$geoWithin', $shape);
566
    }
567
568
    /**
569
     * Return the current field.
570
     */
571 2
    public function getCurrentField() : ?string
572
    {
573 2
        return $this->currentField;
574
    }
575
576
    /**
577
     * Gets prepared newObj part of expression.
578
     */
579 179
    public function getNewObj() : array
580
    {
581 179
        return $this->dm->getUnitOfWork()
582 179
            ->getDocumentPersister($this->class->name)
583 179
            ->prepareQueryOrNewObj($this->newObj, true);
584
    }
585
586
    /**
587
     * Gets prepared query part of expression.
588
     */
589 254
    public function getQuery() : array
590
    {
591 254
        return $this->dm->getUnitOfWork()
592 254
            ->getDocumentPersister($this->class->name)
593 254
            ->prepareQueryOrNewObj($this->query);
594
    }
595
596
    /**
597
     * Specify $gt criteria for the current field.
598
     *
599
     * @see Builder::gt()
600
     * @see http://docs.mongodb.org/manual/reference/operator/gt/
601
     *
602
     * @param mixed $value
603
     */
604 2
    public function gt($value) : self
605
    {
606 2
        return $this->operator('$gt', $value);
607
    }
608
609
    /**
610
     * Specify $gte criteria for the current field.
611
     *
612
     * @see Builder::gte()
613
     * @see http://docs.mongodb.org/manual/reference/operator/gte/
614
     *
615
     * @param mixed $value
616
     */
617 2
    public function gte($value) : self
618
    {
619 2
        return $this->operator('$gte', $value);
620
    }
621
622
    /**
623
     * Specify $in criteria for the current field.
624
     *
625
     * @see Builder::in()
626
     * @see http://docs.mongodb.org/manual/reference/operator/in/
627
     */
628 31
    public function in(array $values) : self
629
    {
630 31
        return $this->operator('$in', array_values($values));
631
    }
632
633
    /**
634
     * Increment the current field.
635
     *
636
     * If the field does not exist, it will be set to this value.
637
     *
638
     * @see Builder::inc()
639
     * @see http://docs.mongodb.org/manual/reference/operator/inc/
640
     *
641
     * @param float|int $value
642
     */
643 5
    public function inc($value) : self
644
    {
645 5
        $this->requiresCurrentField();
646 5
        $this->newObj['$inc'][$this->currentField] = $value;
647 5
        return $this;
648
    }
649
650
    /**
651
     * Checks that the current field includes a reference to the supplied document.
652
     */
653 6
    public function includesReferenceTo(object $document) : self
654
    {
655 6
        $this->requiresCurrentField();
656 6
        $mapping   = $this->getReferenceMapping();
657 4
        $reference = $this->dm->createReference($document, $mapping);
658 4
        $storeAs   = $mapping['storeAs'] ?? null;
659 4
        $keys      = [];
0 ignored issues
show
Unused Code introduced by Gabriel Caruso
$keys is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
660
661
        switch ($storeAs) {
662 4
            case ClassMetadata::REFERENCE_STORE_AS_ID:
663 2
                $this->query[$mapping['name']] = $reference;
664 2
                return $this;
665
                break;
0 ignored issues
show
Unused Code introduced by Andreas Braun
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
666
667 3
            case ClassMetadata::REFERENCE_STORE_AS_REF:
668
                $keys = ['id' => true];
669
                break;
670
671 3
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
672 1
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
673 3
                $keys = ['$ref' => true, '$id' => true, '$db' => true];
674
675 3
                if ($storeAs === ClassMetadata::REFERENCE_STORE_AS_DB_REF) {
676 2
                    unset($keys['$db']);
677
                }
678
679 3
                if (isset($mapping['targetDocument'])) {
680 1
                    unset($keys['$ref'], $keys['$db']);
681
                }
682 3
                break;
683
684
            default:
685
                throw new InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
686
        }
687
688 3
        foreach ($keys as $key => $value) {
689 3
            $this->query[$mapping['name']]['$elemMatch'][$key] = $reference[$key];
690
        }
691
692 3
        return $this;
693
    }
694
695
    /**
696
     * Set the $language option for $text criteria.
697
     *
698
     * This method must be called after text().
699
     *
700
     * @see Builder::language()
701
     * @see http://docs.mongodb.org/manual/reference/operator/text/
702
     *
703
     * @throws BadMethodCallException If the query does not already have $text criteria.
704
     */
705 2
    public function language(string $language) : self
706
    {
707 2
        if (! isset($this->query['$text'])) {
708 1
            throw new BadMethodCallException('This method requires a $text operator (call text() first)');
709
        }
710
711 1
        $this->query['$text']['$language'] = $language;
712
713 1
        return $this;
714
    }
715
716
    /**
717
     * Specify $lt criteria for the current field.
718
     *
719
     * @see Builder::lte()
720
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
721
     *
722
     * @param mixed $value
723
     */
724 4
    public function lt($value) : self
725
    {
726 4
        return $this->operator('$lt', $value);
727
    }
728
729
    /**
730
     * Specify $lte criteria for the current field.
731
     *
732
     * @see Builder::lte()
733
     * @see http://docs.mongodb.org/manual/reference/operator/lte/
734
     *
735
     * @param mixed $value
736
     */
737 2
    public function lte($value) : self
738
    {
739 2
        return $this->operator('$lte', $value);
740
    }
741
742
    /**
743
     * Updates the value of the field to a specified value if the specified value is greater than the current value of the field.
744
     *
745
     * @see Builder::max()
746
     * @see http://docs.mongodb.org/manual/reference/operator/update/max/
747
     *
748
     * @param mixed $value
749
     */
750
    public function max($value) : self
751
    {
752
        $this->requiresCurrentField();
753
        $this->newObj['$max'][$this->currentField] = $value;
754
        return $this;
755
    }
756
757
    /**
758
     * Updates the value of the field to a specified value if the specified value is less than the current value of the field.
759
     *
760
     * @see Builder::min()
761
     * @see http://docs.mongodb.org/manual/reference/operator/update/min/
762
     *
763
     * @param mixed $value
764
     */
765
    public function min($value) : self
766
    {
767
        $this->requiresCurrentField();
768
        $this->newObj['$min'][$this->currentField] = $value;
769
        return $this;
770
    }
771
772
    /**
773
     * Specify $mod criteria for the current field.
774
     *
775
     * @see Builder::mod()
776
     * @see http://docs.mongodb.org/manual/reference/operator/mod/
777
     *
778
     * @param float|int $divisor
779
     * @param float|int $remainder
780
     */
781
    public function mod($divisor, $remainder = 0) : self
782
    {
783
        return $this->operator('$mod', [$divisor, $remainder]);
784
    }
785
786
    /**
787
     * Multiply the current field.
788
     *
789
     * If the field does not exist, it will be set to 0.
790
     *
791
     * @see Builder::mul()
792
     * @see http://docs.mongodb.org/manual/reference/operator/mul/
793
     *
794
     * @param float|int $value
795
     */
796
    public function mul($value) : self
797
    {
798
        $this->requiresCurrentField();
799
        $this->newObj['$mul'][$this->currentField] = $value;
800
        return $this;
801
    }
802
803
    /**
804
     * Add $near criteria to the expression.
805
     *
806
     * A GeoJSON point may be provided as the first and only argument for
807
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
808
     * an array corresponding to the point's JSON representation.
809
     *
810
     * @see Builder::near()
811
     * @see http://docs.mongodb.org/manual/reference/operator/near/
812
     *
813
     * @param float|array|Point $x
814
     * @param float             $y
815
     */
816 3
    public function near($x, $y = null) : self
817
    {
818 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by Andreas Braun
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
819 1
            $x = $x->jsonSerialize();
820
        }
821
822 3
        if (is_array($x)) {
823 2
            return $this->operator('$near', ['$geometry' => $x]);
824
        }
825
826 1
        return $this->operator('$near', [$x, $y]);
827
    }
828
829
    /**
830
     * Add $nearSphere criteria to the expression.
831
     *
832
     * A GeoJSON point may be provided as the first and only argument for
833
     * 2dsphere queries. This single parameter may be a GeoJSON point object or
834
     * an array corresponding to the point's JSON representation.
835
     *
836
     * @see Builder::nearSphere()
837
     * @see http://docs.mongodb.org/manual/reference/operator/nearSphere/
838
     *
839
     * @param float|array|Point $x
840
     * @param float             $y
841
     */
842 3
    public function nearSphere($x, $y = null) : self
843
    {
844 3
        if ($x instanceof Point) {
0 ignored issues
show
Bug introduced by Andreas Braun
The class GeoJson\Geometry\Point does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
845 1
            $x = $x->jsonSerialize();
846
        }
847
848 3
        if (is_array($x)) {
849 2
            return $this->operator('$nearSphere', ['$geometry' => $x]);
850
        }
851
852 1
        return $this->operator('$nearSphere', [$x, $y]);
853
    }
854
855
    /**
856
     * Negates an expression for the current field.
857
     *
858
     * @see Builder::not()
859
     * @see http://docs.mongodb.org/manual/reference/operator/not/
860
     *
861
     * @param array|Expr $expression
862
     */
863 2
    public function not($expression) : self
864
    {
865 2
        return $this->operator('$not', $expression instanceof Expr ? $expression->getQuery() : $expression);
866
    }
867
868
    /**
869
     * Specify $ne criteria for the current field.
870
     *
871
     * @see Builder::notEqual()
872
     * @see http://docs.mongodb.org/manual/reference/operator/ne/
873
     *
874
     * @param mixed $value
875
     */
876 6
    public function notEqual($value) : self
877
    {
878 6
        return $this->operator('$ne', $value);
879
    }
880
881
    /**
882
     * Specify $nin criteria for the current field.
883
     *
884
     * @see Builder::notIn()
885
     * @see http://docs.mongodb.org/manual/reference/operator/nin/
886
     */
887 6
    public function notIn(array $values) : self
888
    {
889 6
        return $this->operator('$nin', array_values($values));
890
    }
891
892
    /**
893
     * Defines an operator and value on the expression.
894
     *
895
     * If there is a current field, the operator will be set on it; otherwise,
896
     * the operator is set at the top level of the query.
897
     *
898
     * @param mixed $value
899
     */
900 81
    public function operator(string $operator, $value) : self
901
    {
902 81
        $this->wrapEqualityCriteria();
903
904 81
        if ($this->currentField) {
0 ignored issues
show
Bug Best Practice introduced by Andreas Braun
The expression $this->currentField of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch 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:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
905 56
            $this->query[$this->currentField][$operator] = $value;
906
        } else {
907 27
            $this->query[$operator] = $value;
908
        }
909 81
        return $this;
910
    }
911
912
    /**
913
     * Remove the first element from the current array field.
914
     *
915
     * @see Builder::popFirst()
916
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
917
     */
918 2
    public function popFirst() : self
919
    {
920 2
        $this->requiresCurrentField();
921 2
        $this->newObj['$pop'][$this->currentField] = -1;
922 2
        return $this;
923
    }
924
925
    /**
926
     * Remove the last element from the current array field.
927
     *
928
     * @see Builder::popLast()
929
     * @see http://docs.mongodb.org/manual/reference/operator/pop/
930
     */
931 1
    public function popLast() : self
932
    {
933 1
        $this->requiresCurrentField();
934 1
        $this->newObj['$pop'][$this->currentField] = 1;
935 1
        return $this;
936
    }
937
938
    /**
939
     * Add $position criteria to the expression for a $push operation.
940
     *
941
     * This is useful in conjunction with {@link Expr::each()} for a
942
     * {@link Expr::push()} operation.
943
     *
944
     * @see http://docs.mongodb.org/manual/reference/operator/update/position/
945
     */
946 1
    public function position(int $position) : self
947
    {
948 1
        return $this->operator('$position', $position);
949
    }
950
951
    /**
952
     * Remove all elements matching the given value or expression from the
953
     * current array field.
954
     *
955
     * @see Builder::pull()
956
     * @see http://docs.mongodb.org/manual/reference/operator/pull/
957
     *
958
     * @param mixed|Expr $valueOrExpression
959
     */
960 2
    public function pull($valueOrExpression) : self
961
    {
962 2
        if ($valueOrExpression instanceof Expr) {
963 1
            $valueOrExpression = $valueOrExpression->getQuery();
964
        }
965
966 2
        $this->requiresCurrentField();
967 2
        $this->newObj['$pull'][$this->currentField] = $valueOrExpression;
968 2
        return $this;
969
    }
970
971
    /**
972
     * Remove all elements matching any of the given values from the current
973
     * array field.
974
     *
975
     * @see Builder::pullAll()
976
     * @see http://docs.mongodb.org/manual/reference/operator/pullAll/
977
     */