Completed
Push — 2.0 ( 1a4617...9eea50 )
by Peter
07:42 queued 11s
created

Spec   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 617
Duplicated Lines 4.21 %

Coupling/Cohesion

Components 0
Dependencies 47

Importance

Changes 0
Metric Value
wmc 58
lcom 0
cbo 47
dl 26
loc 617
rs 4.543
c 0
b 0
f 0

51 Methods

Rating   Name   Duplication   Size   Complexity  
A __callStatic() 0 12 3
A not() 0 4 1
A andX() 13 14 2
A orX() 13 14 2
A join() 0 4 1
A leftJoin() 0 4 1
A innerJoin() 0 4 1
A indexBy() 0 4 1
A limit() 0 4 1
A offset() 0 4 1
A slice() 0 4 1
A orderBy() 0 4 1
A groupBy() 0 4 1
A distinct() 0 4 1
A select() 0 5 1
A addSelect() 0 5 1
A selectEntity() 0 4 1
A selectAs() 0 4 1
A selectHiddenAs() 0 4 1
A asArray() 0 4 1
A asSingleScalar() 0 4 1
A asScalar() 0 4 1
A cache() 0 4 1
A roundDateTimeParams() 0 4 1
A isNull() 0 4 1
A isNotNull() 0 4 1
A in() 0 4 1
A notIn() 0 4 1
A eq() 0 4 1
A neq() 0 4 1
A lt() 0 4 1
A lte() 0 4 1
A gt() 0 4 1
A gte() 0 4 1
A like() 0 4 1
A instanceOfX() 0 4 1
A memberOfX() 0 4 1
A countOf() 0 4 1
A having() 0 10 3
A field() 0 4 1
A value() 0 4 1
A values() 0 4 1
A likePattern() 0 4 1
A countDistinct() 0 4 1
A add() 0 4 1
A sub() 0 4 1
A mul() 0 4 1
A div() 0 4 1
A mod() 0 4 1
A fun() 0 12 2
A alias() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Spec 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 Spec, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification;
16
17
use Happyr\DoctrineSpecification\Filter\Equals;
18
use Happyr\DoctrineSpecification\Filter\Filter;
19
use Happyr\DoctrineSpecification\Filter\GreaterOrEqualThan;
20
use Happyr\DoctrineSpecification\Filter\GreaterThan;
21
use Happyr\DoctrineSpecification\Filter\In;
22
use Happyr\DoctrineSpecification\Filter\InstanceOfX;
23
use Happyr\DoctrineSpecification\Filter\IsNotNull;
24
use Happyr\DoctrineSpecification\Filter\IsNull;
25
use Happyr\DoctrineSpecification\Filter\LessOrEqualThan;
26
use Happyr\DoctrineSpecification\Filter\LessThan;
27
use Happyr\DoctrineSpecification\Filter\Like;
28
use Happyr\DoctrineSpecification\Filter\MemberOfX;
29
use Happyr\DoctrineSpecification\Filter\NotEquals;
30
use Happyr\DoctrineSpecification\Logic\AndX;
31
use Happyr\DoctrineSpecification\Logic\Not;
32
use Happyr\DoctrineSpecification\Logic\OrX;
33
use Happyr\DoctrineSpecification\Operand\Addition;
34
use Happyr\DoctrineSpecification\Operand\Alias;
35
use Happyr\DoctrineSpecification\Operand\CountDistinct;
36
use Happyr\DoctrineSpecification\Operand\Division;
37
use Happyr\DoctrineSpecification\Operand\Field;
38
use Happyr\DoctrineSpecification\Operand\LikePattern;
39
use Happyr\DoctrineSpecification\Operand\Modulo;
40
use Happyr\DoctrineSpecification\Operand\Multiplication;
41
use Happyr\DoctrineSpecification\Operand\Operand;
42
use Happyr\DoctrineSpecification\Operand\PlatformFunction;
43
use Happyr\DoctrineSpecification\Operand\Subtraction;
44
use Happyr\DoctrineSpecification\Operand\Value;
45
use Happyr\DoctrineSpecification\Operand\Values;
46
use Happyr\DoctrineSpecification\Query\AddSelect;
47
use Happyr\DoctrineSpecification\Query\Distinct;
48
use Happyr\DoctrineSpecification\Query\GroupBy;
49
use Happyr\DoctrineSpecification\Query\IndexBy;
50
use Happyr\DoctrineSpecification\Query\InnerJoin;
51
use Happyr\DoctrineSpecification\Query\Join;
52
use Happyr\DoctrineSpecification\Query\LeftJoin;
53
use Happyr\DoctrineSpecification\Query\Limit;
54
use Happyr\DoctrineSpecification\Query\Offset;
55
use Happyr\DoctrineSpecification\Query\OrderBy;
56
use Happyr\DoctrineSpecification\Query\QueryModifier;
57
use Happyr\DoctrineSpecification\Query\Select;
58
use Happyr\DoctrineSpecification\Query\Selection\SelectAs;
59
use Happyr\DoctrineSpecification\Query\Selection\SelectEntity;
60
use Happyr\DoctrineSpecification\Query\Selection\SelectHiddenAs;
61
use Happyr\DoctrineSpecification\Query\Slice;
62
use Happyr\DoctrineSpecification\Result\AsArray;
63
use Happyr\DoctrineSpecification\Result\AsScalar;
64
use Happyr\DoctrineSpecification\Result\AsSingleScalar;
65
use Happyr\DoctrineSpecification\Result\Cache;
66
use Happyr\DoctrineSpecification\Result\RoundDateTime;
67
use Happyr\DoctrineSpecification\Specification\CountOf;
68
use Happyr\DoctrineSpecification\Specification\Having;
69
70
/**
71
 * Factory class for the specifications.
72
 *
73
 * @method static PlatformFunction CONCAT($str1, $str2)
74
 * @method static PlatformFunction SUBSTRING($str, $start, $length = null) Return substring of given string.
75
 * @method static PlatformFunction TRIM($str) Trim the string by the given trim char, defaults to whitespaces.
76
 * @method static PlatformFunction LOWER($str) Returns the string lowercased.
77
 * @method static PlatformFunction UPPER($str) Return the upper-case of the given string.
78
 * @method static PlatformFunction IDENTITY($expression, $fieldMapping = null) Retrieve the foreign key column of association of the owning side
79
 * @method static PlatformFunction LENGTH($str) Returns the length of the given string
80
 * @method static PlatformFunction LOCATE($needle, $haystack, $offset = 0) Locate the first occurrence of the substring in the string.
81
 * @method static PlatformFunction ABS($expression)
82
 * @method static PlatformFunction SQRT($q) Return the square-root of q.
83
 * @method static PlatformFunction MOD($a, $b) Return a MOD b.
84
 * @method static PlatformFunction SIZE($collection) Return the number of elements in the specified collection
85
 * @method static PlatformFunction DATE_DIFF($date1, $date2) Calculate the difference in days between date1-date2.
86
 * @method static PlatformFunction BIT_AND($a, $b)
87
 * @method static PlatformFunction BIT_OR($a, $b)
88
 * @method static PlatformFunction MIN($a)
89
 * @method static PlatformFunction MAX($a)
90
 * @method static PlatformFunction AVG($a)
91
 * @method static PlatformFunction SUM($a)
92
 * @method static PlatformFunction COUNT($a)
93
 * @method static PlatformFunction CURRENT_DATE() Return the current date
94
 * @method static PlatformFunction CURRENT_TIME() Returns the current time
95
 * @method static PlatformFunction CURRENT_TIMESTAMP() Returns a timestamp of the current date and time.
96
 * @method static PlatformFunction DATE_ADD($date, $days, $unit) Add the number of days to a given date. (Supported units are DAY, MONTH)
97
 * @method static PlatformFunction DATE_SUB($date, $days, $unit) Substract the number of days from a given date. (Supported units are DAY, MONTH)
98
 */
99
class Spec
100
{
101
    /**
102
     * @param string $name
103
     * @param array  $arguments
104
     *
105
     * @return PlatformFunction
106
     */
107
    public static function __callStatic($name, array $arguments = [])
108
    {
109
        // allow use array in arguments of static function
110
        // Spec::DATE_DIFF([$date1, $date2]);
111
        // is equal
112
        // Spec::DATE_DIFF($date1, $date2);
113
        if (1 === count($arguments) && is_array(current($arguments))) {
114
            $arguments = current($arguments);
115
        }
116
117
        return self::fun($name, $arguments);
118
    }
119
120
    // Logic
121
122
    /**
123
     * @return AndX
124
     */
125 View Code Duplication
    public static function andX()
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...
126
    {
127
        // NEXT_MAJOR: use variable-length argument lists (...$specs)
128
        $spec = (new \ReflectionClass(AndX::class))->newInstanceArgs(func_get_args());
129
130
        // hook for PHPStan
131
        if (!($spec instanceof AndX)) {
132
            throw new \RuntimeException(
133
                sprintf('The specification must be an instance of "%s", but got "%s".', AndX::class, get_class($spec))
134
            );
135
        }
136
137
        return $spec;
138
    }
139
140
    /**
141
     * @return OrX
142
     */
143 View Code Duplication
    public static function orX()
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...
144
    {
145
        // NEXT_MAJOR: use variable-length argument lists (...$specs)
146
        $spec = (new \ReflectionClass(OrX::class))->newInstanceArgs(func_get_args());
147
148
        // hook for PHPStan
149
        if (!($spec instanceof OrX)) {
150
            throw new \RuntimeException(
151
                sprintf('The specification must be an instance of "%s", but got "%s".', OrX::class, get_class($spec))
152
            );
153
        }
154
155
        return $spec;
156
    }
157
158
    /**
159
     * @param Filter $spec
160
     *
161
     * @return Not
162
     */
163
    public static function not(Filter $spec)
164
    {
165
        return new Not($spec);
166
    }
167
168
    // Query modifier
169
170
    /**
171
     * @param string      $field
172
     * @param string      $newAlias
173
     * @param string|null $dqlAlias
174
     *
175
     * @return Join
176
     */
177
    public static function join($field, $newAlias, $dqlAlias = null)
178
    {
179
        return new Join($field, $newAlias, $dqlAlias);
180
    }
181
182
    /**
183
     * @param string      $field
184
     * @param string      $newAlias
185
     * @param string|null $dqlAlias
186
     *
187
     * @return LeftJoin
188
     */
189
    public static function leftJoin($field, $newAlias, $dqlAlias = null)
190
    {
191
        return new LeftJoin($field, $newAlias, $dqlAlias);
192
    }
193
194
    /**
195
     * @param string      $field
196
     * @param string      $newAlias
197
     * @param string|null $dqlAlias
198
     *
199
     * @return InnerJoin
200
     */
201
    public static function innerJoin($field, $newAlias, $dqlAlias = null)
202
    {
203
        return new InnerJoin($field, $newAlias, $dqlAlias);
204
    }
205
206
    /**
207
     * @param Field|string $field
208
     * @param string|null  $dqlAlias
209
     *
210
     * @return IndexBy
211
     */
212
    public static function indexBy($field, $dqlAlias = null)
213
    {
214
        return new IndexBy($field, $dqlAlias);
215
    }
216
217
    /**
218
     * @param int $count
219
     *
220
     * @return Limit
221
     */
222
    public static function limit($count)
223
    {
224
        return new Limit($count);
225
    }
226
227
    /**
228
     * @param int $count
229
     *
230
     * @return Offset
231
     */
232
    public static function offset($count)
233
    {
234
        return new Offset($count);
235
    }
236
237
    /**
238
     * @param int $sliceSize
239
     * @param int $sliceNumber
240
     *
241
     * @return Slice
242
     */
243
    public static function slice($sliceSize, $sliceNumber = 0)
244
    {
245
        return new Slice($sliceSize, $sliceNumber);
246
    }
247
248
    /**
249
     * @param Field|Alias|string $field
250
     * @param string             $order
251
     * @param string|null        $dqlAlias
252
     *
253
     * @return OrderBy
254
     */
255
    public static function orderBy($field, $order = 'ASC', $dqlAlias = null)
256
    {
257
        return new OrderBy($field, $order, $dqlAlias);
258
    }
259
260
    /**
261
     * @param Field|Alias|string $field
262
     * @param string|null        $dqlAlias
263
     *
264
     * @return GroupBy
265
     */
266
    public static function groupBy($field, $dqlAlias = null)
267
    {
268
        return new GroupBy($field, $dqlAlias);
269
    }
270
271
    /**
272
     * @return Distinct
273
     */
274
    public static function distinct()
275
    {
276
        return new Distinct();
277
    }
278
279
    // Selection
280
281
    /**
282
     * @param mixed $field
283
     *
284
     * @return Select
285
     */
286
    public static function select($field)
0 ignored issues
show
Unused Code introduced by
The parameter $field 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...
287
    {
288
        // NEXT_MAJOR: use variable-length argument lists (...$fields)
289
        return new Select(func_get_args());
290
    }
291
292
    /**
293
     * @param mixed $field
294
     *
295
     * @return AddSelect
296
     */
297
    public static function addSelect($field)
0 ignored issues
show
Unused Code introduced by
The parameter $field 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...
298
    {
299
        // NEXT_MAJOR: use variable-length argument lists (...$fields)
300
        return new AddSelect(func_get_args());
301
    }
302
303
    /**
304
     * @param string $dqlAlias
305
     *
306
     * @return SelectEntity
307
     */
308
    public static function selectEntity($dqlAlias)
309
    {
310
        return new SelectEntity($dqlAlias);
311
    }
312
313
    /**
314
     * @param Filter|Operand|string $expression
315
     * @param string                $alias
316
     *
317
     * @return SelectAs
318
     */
319
    public static function selectAs($expression, $alias)
320
    {
321
        return new SelectAs($expression, $alias);
322
    }
323
324
    /**
325
     * @param Filter|Operand|string $expression
326
     * @param string                $alias
327
     *
328
     * @return SelectHiddenAs
329
     */
330
    public static function selectHiddenAs($expression, $alias)
331
    {
332
        return new SelectHiddenAs($expression, $alias);
333
    }
334
335
    // Result modifier
336
337
    /**
338
     * @return AsArray
339
     */
340
    public static function asArray()
341
    {
342
        return new AsArray();
343
    }
344
345
    /**
346
     * @return AsSingleScalar
347
     */
348
    public static function asSingleScalar()
349
    {
350
        return new AsSingleScalar();
351
    }
352
353
    /**
354
     * @return AsScalar
355
     */
356
    public static function asScalar()
357
    {
358
        return new AsScalar();
359
    }
360
361
    /**
362
     * @param int $cacheLifetime How many seconds the cached entry is valid
363
     *
364
     * @return Cache
365
     */
366
    public static function cache($cacheLifetime)
367
    {
368
        return new Cache($cacheLifetime);
369
    }
370
371
    /**
372
     * @param int $roundSeconds How may seconds to round time
373
     *
374
     * @return RoundDateTime
375
     */
376
    public static function roundDateTimeParams($roundSeconds)
377
    {
378
        return new RoundDateTime($roundSeconds);
379
    }
380
381
    // Filters
382
383
    /**
384
     * @param Operand|string $field
385
     * @param string|null    $dqlAlias
386
     *
387
     * @return IsNull
388
     */
389
    public static function isNull($field, $dqlAlias = null)
390
    {
391
        return new IsNull($field, $dqlAlias);
392
    }
393
394
    /**
395
     * @param Operand|string $field
396
     * @param string|null    $dqlAlias
397
     *
398
     * @return IsNotNull
399
     */
400
    public static function isNotNull($field, $dqlAlias = null)
401
    {
402
        return new IsNotNull($field, $dqlAlias);
403
    }
404
405
    /**
406
     * Make sure the $field has a value equals to $value.
407
     *
408
     * @param Operand|string $field
409
     * @param Operand|mixed  $value
410
     * @param string|null    $dqlAlias
411
     *
412
     * @return In
413
     */
414
    public static function in($field, $value, $dqlAlias = null)
415
    {
416
        return new In($field, $value, $dqlAlias);
417
    }
418
419
    /**
420
     * @param Operand|string $field
421
     * @param Operand|mixed  $value
422
     * @param string|null    $dqlAlias
423
     *
424
     * @return Not
425
     */
426
    public static function notIn($field, $value, $dqlAlias = null)
427
    {
428
        return new Not(new In($field, $value, $dqlAlias));
429
    }
430
431
    /**
432
     * @param Operand|string $field
433
     * @param Operand|mixed  $value
434
     * @param string|null    $dqlAlias
435
     *
436
     * @return Equals
437
     */
438
    public static function eq($field, $value, $dqlAlias = null)
439
    {
440
        return new Equals($field, $value, $dqlAlias);
441
    }
442
443
    /**
444
     * @param Operand|string $field
445
     * @param Operand|mixed  $value
446
     * @param string|null    $dqlAlias
447
     *
448
     * @return NotEquals
449
     */
450
    public static function neq($field, $value, $dqlAlias = null)
451
    {
452
        return new NotEquals($field, $value, $dqlAlias);
453
    }
454
455
    /**
456
     * @param Operand|string $field
457
     * @param Operand|mixed  $value
458
     * @param string|null    $dqlAlias
459
     *
460
     * @return LessThan
461
     */
462
    public static function lt($field, $value, $dqlAlias = null)
463
    {
464
        return new LessThan($field, $value, $dqlAlias);
465
    }
466
467
    /**
468
     * @param Operand|string $field
469
     * @param Operand|mixed  $value
470
     * @param string|null    $dqlAlias
471
     *
472
     * @return LessOrEqualThan
473
     */
474
    public static function lte($field, $value, $dqlAlias = null)
475
    {
476
        return new LessOrEqualThan($field, $value, $dqlAlias);
477
    }
478
479
    /**
480
     * @param Operand|string $field
481
     * @param Operand|mixed  $value
482
     * @param string|null    $dqlAlias
483
     *
484
     * @return GreaterThan
485
     */
486
    public static function gt($field, $value, $dqlAlias = null)
487
    {
488
        return new GreaterThan($field, $value, $dqlAlias);
489
    }
490
491
    /**
492
     * @param Operand|string $field
493
     * @param Operand|mixed  $value
494
     * @param string|null    $dqlAlias
495
     *
496
     * @return GreaterOrEqualThan
497
     */
498
    public static function gte($field, $value, $dqlAlias = null)
499
    {
500
        return new GreaterOrEqualThan($field, $value, $dqlAlias);
501
    }
502
503
    /**
504
     * @param Operand|string     $field
505
     * @param LikePattern|string $value
506
     * @param string             $format
507
     * @param string|null        $dqlAlias
508
     *
509
     * @return Like
510
     */
511
    public static function like($field, $value, $format = Like::CONTAINS, $dqlAlias = null)
512
    {
513
        return new Like($field, $value, $format, $dqlAlias);
514
    }
515
516
    /**
517
     * @param string      $value
518
     * @param string|null $dqlAlias
519
     *
520
     * @return InstanceOfX
521
     */
522
    public static function instanceOfX($value, $dqlAlias = null)
523
    {
524
        return new InstanceOfX($value, $dqlAlias);
525
    }
526
527
    /**
528
     * @param Operand|string $value
529
     * @param Operand|string $field
530
     * @param string|null    $dqlAlias
531
     *
532
     * @return MemberOfX
533
     */
534
    public static function memberOfX($value, $field, $dqlAlias = null)
535
    {
536
        return new MemberOfX($value, $field, $dqlAlias);
537
    }
538
539
    // Specifications
540
541
    /**
542
     * @param Filter|QueryModifier $spec
543
     *
544
     * @return CountOf
545
     */
546
    public static function countOf($spec)
547
    {
548
        return new CountOf($spec);
549
    }
550
551
    /**
552
     * @param Filter|QueryModifier|string $spec
553
     *
554
     * @return Having
555
     */
556
    public static function having($spec)
557
    {
558
        if (!($spec instanceof Filter)) {
559
            @trigger_error('Using "'.(is_object($spec) ? get_class($spec) : gettype($spec)).'" as argument in '.__METHOD__.' method is deprecated since version 1.1 and will not be possible in 2.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
560
        }
561
562
        // NEXT_MAJOR: use here \Happyr\DoctrineSpecification\Query\Having
563
564
        return new Having($spec);
0 ignored issues
show
Deprecated Code introduced by
The class Happyr\DoctrineSpecification\Specification\Having has been deprecated with message: This class is deprecated since version 1.1 and will be removed in 2.0, use \Happyr\DoctrineSpecification\Query\Having instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
565
    }
566
567
    // Operands
568
569
    /**
570
     * @param string      $fieldName
571
     * @param string|null $dqlAlias
572
     *
573
     * @return Field
574
     */
575
    public static function field($fieldName, $dqlAlias = null)
576
    {
577
        return new Field($fieldName, $dqlAlias);
578
    }
579
580
    /**
581
     * @param mixed           $value
582
     * @param int|string|null $valueType
583
     *
584
     * @return Value
585
     */
586
    public static function value($value, $valueType = null)
587
    {
588
        return new Value($value, $valueType);
589
    }
590
591
    /**
592
     * @param array           $values
593
     * @param int|string|null $valueType
594
     *
595
     * @return Values
596
     */
597
    public static function values($values, $valueType = null)
598
    {
599
        return new Values($values, $valueType);
600
    }
601
602
    /**
603
     * @param string $value
604
     * @param string $format
605
     *
606
     * @return LikePattern
607
     */
608
    public static function likePattern($value, $format = LikePattern::CONTAINS)
609
    {
610
        return new LikePattern($value, $format);
611
    }
612
613
    /**
614
     * @param Operand|string $field
615
     *
616
     * @return CountDistinct
617
     */
618
    public static function countDistinct($field)
619
    {
620
        return new CountDistinct($field);
621
    }
622
623
    // Arithmetic operands
624
625
    /**
626
     * @param Operand|string $field
627
     * @param Operand|mixed  $value
628
     *
629
     * @return Addition
630
     */
631
    public static function add($field, $value)
632
    {
633
        return new Addition($field, $value);
634
    }
635
636
    /**
637
     * @param Operand|string $field
638
     * @param Operand|mixed  $value
639
     *
640
     * @return Subtraction
641
     */
642
    public static function sub($field, $value)
643
    {
644
        return new Subtraction($field, $value);
645
    }
646
647
    /**
648
     * @param Operand|string $field
649
     * @param Operand|mixed  $value
650
     *
651
     * @return Multiplication
652
     */
653
    public static function mul($field, $value)
654
    {
655
        return new Multiplication($field, $value);
656
    }
657
658
    /**
659
     * @param Operand|string $field
660
     * @param Operand|mixed  $value
661
     *
662
     * @return Division
663
     */
664
    public static function div($field, $value)
665
    {
666
        return new Division($field, $value);
667
    }
668
669
    /**
670
     * @param Operand|string $field
671
     * @param Operand|mixed  $value
672
     *
673
     * @return Modulo
674
     */
675
    public static function mod($field, $value)
676
    {
677
        return new Modulo($field, $value);
678
    }
679
680
    /**
681
     * Call DQL function.
682
     *
683
     * Usage:
684
     *  Spec::fun('CURRENT_DATE')
685
     *  Spec::fun('DATE_DIFF', $date1, $date2)
686
     *  Spec::fun('DATE_DIFF', [$date1, $date2])
687
     *
688
     * @param string $functionName
689
     * @param mixed  $arguments
690
     *
691
     * @return PlatformFunction
692
     */
693
    public static function fun($functionName, $arguments = [])
694
    {
695
        // NEXT_MAJOR: use variable-length argument lists ($functionName, ...$arguments)
696
        if (2 === func_num_args()) {
697
            $arguments = (array) $arguments;
698
        } else {
699
            $arguments = func_get_args();
700
            $functionName = array_shift($arguments);
701
        }
702
703
        return new PlatformFunction($functionName, $arguments);
704
    }
705
706
    /**
707
     * @param string $alias
708
     *
709
     * @return Alias
710
     */
711
    public static function alias($alias)
712
    {
713
        return new Alias($alias);
714
    }
715
}
716