FunctionDescription::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
namespace POData\UriProcessor\QueryProcessor;
4
5
use POData\Common\ODataException;
6
use POData\Common\Messages;
7
use POData\Common\ODataConstants;
8
use POData\Providers\Metadata\Type\NullType;
9
use POData\Providers\Metadata\Type\Int64;
10
use POData\Providers\Metadata\Type\Int16;
11
use POData\Providers\Metadata\Type\Guid;
12
use POData\Providers\Metadata\Type\Single;
13
use POData\Providers\Metadata\Type\Double;
14
use POData\Providers\Metadata\Type\Decimal;
15
use POData\Providers\Metadata\Type\DateTime;
16
use POData\Providers\Metadata\Type\DateTimeTz;
17
use POData\Providers\Metadata\Type\Int32;
18
use POData\Providers\Metadata\Type\StringType;
19
use POData\Providers\Metadata\Type\ArrayType;
20
use POData\Providers\Metadata\Type\Boolean;
21
use POData\Providers\Metadata\Type\Binary;
22
use POData\Providers\Metadata\Type\IType;
23
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\AbstractExpression;
24
use POData\UriProcessor\QueryProcessor\ExpressionParser\ExpressionToken;
25
26
/**
27
 * Class FunctionDescription
28
 *
29
 * Class to represent function signature including function-name
30
 *
31
 * @package POData\UriProcessor\QueryProcessor\FunctionDescription
32
 */
33
class FunctionDescription
34
{
35
    /**
36
     * @var string
37
     */
38
    public $name;
39
40
    /**
41
     * @var IType
42
     */
43
    public $returnType;
44
45
    /**
46
     * @var IType[]
47
     */
48
    public $argumentTypes;
49
50
    /**
51
     * Create new instance of FunctionDescription
52
     *
53
     * @param string  $name  Name of the function
54
     * @param IType   $returnType    Return type
55
     * @param IType[] $argumentTypes Parameter type
56
     */
57 52
    public function __construct($name, $returnType, $argumentTypes)
58
    {
59 52
        $this->name = $name;
60 52
        $this->returnType = $returnType;
61 52
        $this->argumentTypes = $argumentTypes;
62
    }
63
64
    /**
65
     * Get the function prototype as string
66
     *
67
     * @return string
68
     */
69 1
    public function getPrototypeAsString()
70
    {
71 1
        $str = $this->returnType->getFullTypeName() . ' ' . $this->name . '(';
72
73 1
        foreach ($this->argumentTypes as $argumentType) {
74 1
            $str .= $argumentType->getFullTypeName() . ', ';
75
        }
76
77 1
        return rtrim($str, ', ') . ')';
78
    }
79
80
    /**
81
     * Create function descriptions for supported function-calls in $filter option
82
     *
83
     * TODO: FIGURE OUT WHAT THE HECK THIS IS RETURNING!?!?
84
     *
85
     * @return array indexed by function name
86
     */
87 35
    public static function filterFunctionDescriptions()
88
    {
89 35
        $functions = array(
90
            //EdmString Functions
91 35
            'endswith'      =>
92 35
                array(
93 35
                    new FunctionDescription(
94 35
                        'endswith', new Boolean(),
95 35
                        array(new StringType(), new StringType())
96 35
                    )
97 35
                ),
98 35
            'indexof'       =>
99 35
                array(
100 35
                    new FunctionDescription(
101 35
                        'indexof', new Int32(),
102 35
                        array(new StringType(), new StringType())
103 35
                    )
104 35
                ),
105 35
            'replace'       =>
106 35
                array(
107 35
                    new FunctionDescription(
108 35
                        'replace', new StringType(),
109 35
                        array(new StringType(), new StringType(), new StringType())
110 35
                    )
111 35
                ),
112 35
            'startswith'    =>
113 35
                array(
114 35
                    new FunctionDescription(
115 35
                        'startswith', new Boolean(),
116 35
                        array(new StringType(), new StringType())
117 35
                    )
118 35
                ),
119 35
            'tolower'       =>
120 35
                array(
121 35
                    new FunctionDescription(
122 35
                        'tolower', new StringType(),
123 35
                        array(new StringType())
124 35
                    )
125 35
                ),
126 35
            'toupper'       =>
127 35
                array(
128 35
                    new FunctionDescription(
129 35
                        'toupper', new StringType(),
130 35
                        array(new StringType())
131 35
                    )
132 35
                ),
133 35
            'trim'          =>
134 35
                array(
135 35
                    new FunctionDescription(
136 35
                        'trim', new StringType(),
137 35
                        array(new StringType())
138 35
                    )
139 35
                ),
140 35
            'substring'     =>
141 35
                array(
142 35
                    new FunctionDescription(
143
144 35
                        'substring', new StringType(),
145 35
                        array(new StringType(), new Int32())
146 35
                    ),
147 35
                    new FunctionDescription(
148 35
                        'substring', new StringType(),
149 35
                        array(new StringType(), new Int32(), new Int32())
150 35
                    )
151 35
                ),
152 35
            'substringof'   =>
153 35
                array(
154 35
                    new FunctionDescription(
155 35
                        'substringof', new Boolean(),
156 35
                        array(new StringType(), new StringType())
157 35
                    )
158 35
                ),
159 35
            'concat'        =>
160 35
                array(
161 35
                    new FunctionDescription(
162 35
                        'concat', new StringType(),
163 35
                        array(new StringType(), new StringType())
164 35
                    )
165 35
                ),
166 35
            'length'        =>
167 35
                array(
168 35
                    new FunctionDescription(
169 35
                        'length', new Int32(),
170 35
                        array(new StringType())
171 35
                    )
172 35
                ),
173
            //DateTime functions
174 35
            'year'          =>
175 35
                array(
176 35
                    new FunctionDescription(
177 35
                        'year', new Int32(),
178 35
                        array(new DateTime())
179 35
                    )
180 35
                ),
181 35
            'month'         =>
182 35
                array(
183 35
                    new FunctionDescription(
184 35
                        'month', new Int32(),
185 35
                        array(new DateTime())
186 35
                    )
187 35
                ),
188 35
            'day'           =>
189 35
                array(
190 35
                    new FunctionDescription(
191 35
                        'day', new Int32(),
192 35
                        array(new DateTime())
193 35
                    )
194 35
                ),
195 35
            'hour'          =>
196 35
                array(
197 35
                    new FunctionDescription(
198 35
                        'hour', new Int32(),
199 35
                        array(new DateTime())
200 35
                    )
201 35
                ),
202 35
            'minute'        =>
203 35
                array(
204 35
                    new FunctionDescription(
205 35
                        'minute', new Int32(),
206 35
                        array(new DateTime())
207 35
                    )
208 35
                ),
209 35
            'second'        =>
210 35
                array(
211 35
                    new FunctionDescription(
212 35
                        'second', new Int32(),
213 35
                        array(new DateTime())
214 35
                    )
215 35
                ),
216
            //Math Functions
217 35
            'round'         =>
218 35
                array(
219 35
                    new FunctionDescription(
220 35
                        'round', new Decimal(),
221 35
                        array(new Decimal())
222 35
                    ),
223 35
                    new FunctionDescription(
224 35
                        'round', new Double(),
225 35
                        array(new Double())
226 35
                    )
227 35
                ),
228 35
            'ceiling'       =>
229 35
                array(
230 35
                    new FunctionDescription(
231 35
                        'ceiling', new Decimal(),
232 35
                        array(new Decimal())
233 35
                    ),
234 35
                    new FunctionDescription(
235 35
                        'ceiling', new Double(),
236 35
                        array(new Double())
237 35
                    )
238 35
                ),
239 35
            'floor'         =>
240 35
                array(
241 35
                    new FunctionDescription(
242 35
                        'floor', new Decimal(),
243 35
                        array(new Decimal())
244 35
                    ),
245 35
                    new FunctionDescription(
246 35
                        'floor', new Double(),
247 35
                        array(new Double())
248 35
                    )
249 35
                )
250 35
            );
251
252 35
        return $functions;
253
    }
254
255
    /**
256
     * Get function description for string comparison
257
     *
258
     * @return FunctionDescription[]
259
     */
260 44
    public static function stringComparisonFunctions()
261
    {
262 44
        return array(
263 44
            new FunctionDescription(
264 44
                'strcmp', new Int32(),
265 44
                array(new StringType(), new StringType())
266 44
            )
267 44
        );
268
    }
269
270
    /**
271
     * Get function description for datetime comparison
272
     *
273
     * @return FunctionDescription[]
274
     */
275
    public static function dateTimeComparisonFunctions()
276
    {
277
        return array(
278
            new FunctionDescription(
279
                'dateTimeCmp', new Int32(),
280
                array(new DateTime(), new DateTime())
281
            )
282
        );
283
    }
284
285
    /**
286
     * Get function description for guid equality check
287
     *
288
     * @return FunctionDescription[]
289
     */
290 1
    public static function guidEqualityFunctions()
291
    {
292 1
        return array(
293 1
            new FunctionDescription(
294 1
                'guidEqual', new Boolean(),
295 1
                array(new Guid(), new Guid())
296 1
            )
297 1
        );
298
    }
299
300
    /**
301
     * Get function description for binary equality check
302
     *
303
     * @return FunctionDescription[]
304
     */
305
    public static function binaryEqualityFunctions()
306
    {
307
        return array(
308
            new FunctionDescription(
309
                'binaryEqual', new Boolean(),
310
                array(new Binary(), new Binary())
311
            )
312
        );
313
    }
314
315
    /**
316
     * Get function descriptions for arithmetic operations
317
     *
318
     * @return FunctionDescription[]
319
     */
320 45
    public static function arithmeticOperationFunctions()
321
    {
322 45
        return array(
323 45
            new FunctionDescription(
324 45
                'F', new int16(),
325 45
                array(new int16(), new int16())
326 45
            ),
327 45
            new FunctionDescription(
328 45
                'F', new int32(),
329 45
                array(new int32(), new int32())
330 45
            ),
331 45
            new FunctionDescription(
332 45
                'F', new int64(),
333 45
                array(new int64(), new int64())
334 45
            ),
335 45
            new FunctionDescription(
336 45
                'F', new Single(),
337 45
                array(new Single(), new Single())
338 45
            ),
339 45
            new FunctionDescription(
340 45
                'F', new Double(),
341 45
                array(new Double(), new Double())
342 45
            ),
343 45
            new FunctionDescription(
344 45
                'F', new Decimal(),
345 45
                array(new Decimal(), new Decimal())
346 45
            )
347 45
        );
348
    }
349
350
    /**
351
     * Get function descriptions for arithmetic add operations
352
     *
353
     * @return FunctionDescription[] indexed by function name
354
     */
355
    public static function addOperationFunctions()
356
    {
357
        return self::arithmeticOperationFunctions();
358
    }
359
360
    /**
361
     * Get function descriptions for arithmetic subtract operations
362
     *
363
     * @return FunctionDescription[] indexed by function name
364
     */
365
    public static function subtractOperationFunctions()
366
    {
367
        return self::arithmeticOperationFunctions();
368
    }
369
370
    /**
371
     * Get function descriptions for logical operations
372
     *
373
     * @return FunctionDescription[]
374
     */
375 6
    public static function logicalOperationFunctions()
376
    {
377 6
        return array(
378 6
            new FunctionDescription(
379 6
                'F', new Boolean(),
380 6
                array(new Boolean(), new Boolean())
381 6
            )
382 6
        );
383
    }
384
385
    /**
386
     * Get function descriptions for relational operations
387
     *
388
     * @return FunctionDescription[]
389
     */
390 44
    public static function relationalOperationFunctions()
391
    {
392 44
        return array_merge(
393 44
            self::arithmeticOperationFunctions(),
394 44
            array(
395 44
                new FunctionDescription(
396 44
                    'F', new Boolean(),
397 44
                    array(new Boolean(), new Boolean())
398 44
                ),
399 44
                new FunctionDescription(
400 44
                    'F', new DateTime(),
401 44
                    array(new DateTime(), new DateTime())
402 44
                ),
403 44
                new FunctionDescription(
404 44
                    'F', new DateTimeTz(),
405 44
                    array(new DateTimeTz(), new DateTimeTz())
406 44
                ),
407 44
                new FunctionDescription(
408 44
                    'F', new DateTimeTz(),
409 44
                    array(new DateTimeTz(), new DateTime())
410 44
                ),
411 44
                new FunctionDescription(
412 44
                    'F', new Guid(),
413 44
                    array(new Guid(), new Guid())
414 44
                ),
415 44
                new FunctionDescription(
416 44
                    'F', new Boolean(),
417 44
                    array(new Binary(), new Binary())
418 44
                ),
419 44
                new FunctionDescription(
420 44
                    'F', new Boolean(),
421 44
                    array(new StringType(), new ArrayType())
422 44
                ),
423 44
                new FunctionDescription(
424 44
                    'F', new Boolean(),
425 44
                    array(new Int32(), new ArrayType())
426 44
                )
427 44
            )
428 44
        );
429
    }
430
431
    /**
432
     * Get function descriptions for unary not operation
433
     *
434
     * @return FunctionDescription[]
435
     */
436 2
    public static function notOperationFunctions()
437
    {
438 2
        return array(
439 2
            new FunctionDescription(
440 2
                'F', new Boolean(),
441 2
                array(new Boolean())
442 2
            )
443 2
        );
444
    }
445
446
    /**
447
     * Get function description for checking an operand is null or not
448
     *
449
     * @param IType $type Type of the argument to null check function.
450
     *
451
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription
452
     */
453 6
    public static function isNullCheckFunction(IType $type)
454
    {
455 6
        return new FunctionDescription('is_null', new Boolean(), array($type));
456
    }
457
458
    /**
459
     * Get function description for unary negate operator
460
     *
461
     * @return FunctionDescription[]
462
     */
463 3
    public static function negateOperationFunctions()
464
    {
465 3
        return array(
466 3
            new FunctionDescription('F', new Int16(), array(new Int16())),
467 3
            new FunctionDescription('F', new Int32(), array(new Int32())),
468 3
            new FunctionDescription('F', new Int64(), array(new Int64())),
469 3
            new FunctionDescription('F', new Single(), array(new Single())),
470 3
            new FunctionDescription('F', new Double(), array(new Double())),
471 3
            new FunctionDescription('F', new Decimal(), array(new Decimal()))
472 3
        );
473
    }
474
475
    /**
476
     * To throw ODataException for incompatible types
477
     *
478
     * @param ExpressionToken           $expressionToken Expression token
479
     * @param AbstractExpression[] $argExpressions  Array of argument expression
480
     *
481
     * @throws ODataException
482
     * @return void
483
     */
484 4
    public static function incompatibleError($expressionToken, $argExpressions)
485
    {
486 4
        $string = null;
487 4
        foreach ($argExpressions as $argExpression) {
488 4
            $string .= $argExpression->getType()->getFullTypeName() . ', ';
489
        }
490
491 4
        $string = rtrim($string, ', ');
0 ignored issues
show
Bug introduced by
It seems like $string can also be of type null; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

491
        $string = rtrim(/** @scrutinizer ignore-type */ $string, ', ');
Loading history...
492 4
        $pos = strrpos($string, ', ');
493 4
        if ($pos !== false) {
494 3
            $string = substr_replace($string, ' and ', strrpos($string, ', '), 2);
495
        }
496
497 4
        throw ODataException::createSyntaxError(
498 4
            Messages::expressionParserInCompatibleTypes(
499 4
                $expressionToken->Text,
500 4
                $string, $expressionToken->Position
0 ignored issues
show
Bug introduced by
It seems like $string can also be of type array; however, parameter $str of POData\Common\Messages::...rserInCompatibleTypes() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

500
                /** @scrutinizer ignore-type */ $string, $expressionToken->Position
Loading history...
501 4
            )
502 4
        );
503
    }
504
505
    /**
506
     * Validate operands of an arithmetic operation and promote if required
507
     *
508
     * @param ExpressionToken    $expressionToken The expression token
509
     * @param AbstractExpression $leftArgument    The left expression
510
     * @param AbstractExpression $rightArgument   The right expression
511
     *
512
     * @return IType
513
     */
514 10
    public static function verifyAndPromoteArithmeticOpArguments($expressionToken,
515
        $leftArgument, $rightArgument
516
    ) {
517 10
        $function
518 10
            = self::findFunctionWithPromotion(
519 10
                self::arithmeticOperationFunctions(),
520 10
                array($leftArgument, $rightArgument)
521 10
            );
522 10
        if ($function == null) {
523 2
            self::incompatibleError(
524 2
                $expressionToken, array($leftArgument, $rightArgument)
525 2
            );
526
        }
527
528 10
        return $function->returnType;
529
    }
530
531
    /**
532
     * Validate operands of an logical operation
533
     *
534
     * @param ExpressionToken    $expressionToken The expression token
535
     * @param AbstractExpression $leftArgument    The left expression
536
     * @param AbstractExpression $rightArgument   The right expression
537
     *
538
     * @return void
539
     *
540
     * @throws ODataException
541
     */
542 6
    public static function verifyLogicalOpArguments($expressionToken,
543
        $leftArgument, $rightArgument
544
    ) {
545 6
        $function = self::findFunctionWithPromotion(
546 6
            self::logicalOperationFunctions(),
547 6
            array($leftArgument, $rightArgument), false
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type POData\Providers\Metadata\Type\Boolean expected by parameter $promoteArguments of POData\UriProcessor\Quer...FunctionWithPromotion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

547
            array($leftArgument, $rightArgument), /** @scrutinizer ignore-type */ false
Loading history...
548 6
        );
549 6
        if ($function == null) {
550 1
            self::incompatibleError(
551 1
                $expressionToken,
552 1
                array($leftArgument, $rightArgument)
553 1
            );
554
        }
555
    }
556
557
    /**
558
     * Validate operands of an relational operation
559
     *
560
     * @param ExpressionToken    $expressionToken The expression token
561
     * @param AbstractExpression $leftArgument    The left argument expression
562
     * @param AbstractExpression $rightArgument   The right argument expression
563
     *
564
     * @return void
565
     */
566 48
    public static function verifyRelationalOpArguments($expressionToken,
567
        $leftArgument, $rightArgument
568
    ) {
569
        //for null operands only equality operators are allowed
570 48
        $null = new NullType();
571 48
        if ($leftArgument->typeIs($null) || $rightArgument->typeIs($null)) {
572 7
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0)
573 7
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
574
            ) {
575 1
                throw ODataException::createSyntaxError(
576 1
                    Messages::expressionParserOperatorNotSupportNull(
577 1
                        $expressionToken->Text,
578 1
                        $expressionToken->Position
579 1
                    )
580 1
                );
581
            }
582
583 7
            return;
584
        }
585
586
        //for guid operands only equality operators are allowed
587 45
        $guid = new Guid();
588 45
        if ($leftArgument->typeIs($guid) && $rightArgument->typeIs($guid)) {
589 1
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0)
590 1
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
591
            ) {
592
                throw ODataException::createSyntaxError(
593
                    Messages::expressionParserOperatorNotSupportGuid(
594
                        $expressionToken->Text, $expressionToken->Position
595
                    )
596
                );
597
            }
598
599 1
            return;
600
        }
601
602
        //for binary operands only equality operators are allowed
603 44
        $binary = new Binary();
604 44
        if ($leftArgument->typeIs($binary) && $rightArgument->typeIs($binary)) {
605
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0)
606
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
607
            ) {
608
                throw ODataException::createSyntaxError(
609
                    Messages::expressionParserOperatorNotSupportBinary(
610
                        $expressionToken->Text, $expressionToken->Position
611
                    )
612
                );
613
            }
614
615
            return;
616
        }
617
618
        //TODO: eq and ne is valid for 'resource reference'
619
        //navigation also verify here
620
621 44
        $functions = array_merge(
622 44
            self::relationalOperationFunctions(),
623 44
            self::stringComparisonFunctions()
624 44
        );
625 44
        $function = self::findFunctionWithPromotion(
626 44
            $functions, array($leftArgument, $rightArgument), false
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type POData\Providers\Metadata\Type\Boolean expected by parameter $promoteArguments of POData\UriProcessor\Quer...FunctionWithPromotion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

626
            $functions, array($leftArgument, $rightArgument), /** @scrutinizer ignore-type */ false
Loading history...
627 44
        );
628 44
        if ($function == null) {
629 1
            self::incompatibleError(
630 1
                $expressionToken,
631 1
                array($leftArgument, $rightArgument)
632 1
            );
633
        }
634
    }
635
636
    /**
637
     * Validate operands of a unary  operation
638
     *
639
     * @param ExpressionToken    $expressionToken The expression token
640
     * @param AbstractExpression $argExpression   Argument expression
641
     *
642
     * @throws ODataException
643
     *
644
     * @return void
645
     */
646 4
    public static function validateUnaryOpArguments($expressionToken, $argExpression)
647
    {
648
        //Unary not
649 4
        if (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT) == 0) {
650 2
            $function = self::findFunctionWithPromotion(
651 2
                self::notOperationFunctions(),
652 2
                array($argExpression)
653 2
            );
654 2
            if ($function == null) {
655 1
                self::incompatibleError($expressionToken, array($argExpression));
656
            }
657
658 2
            return;
659
        }
660
661
        //Unary minus (negation)
662 3
        if (strcmp($expressionToken->Text, '-') == 0) {
663 3
            if (self::findFunctionWithPromotion(self::negateOperationFunctions(), array($argExpression)) == null) {
664 1
                self::incompatibleError($expressionToken, array($argExpression));
665
            }
666
        }
667
    }
668
669
    /**
670
     * Check am identifier is a valid filter function
671
     *
672
     * @param ExpressionToken $expressionToken The expression token
673
     *
674
     * @throws ODataException
675
     *
676
     * @return FunctionDescription[] Array of matching functions
677
     */
678 35
    public static function verifyFunctionExists($expressionToken)
679
    {
680 35
        if (!array_key_exists($expressionToken->Text, self::filterFunctionDescriptions())) {
681 1
            throw ODataException::createSyntaxError(
682 1
                Messages::expressionParserUnknownFunction(
683 1
                    $expressionToken->Text,
684 1
                    $expressionToken->Position
685 1
                )
686 1
            );
687
688
        }
689
690 35
        $filterFunctions = self::filterFunctionDescriptions();
691 35
        return $filterFunctions[$expressionToken->Text];
692
    }
693
694
    /**
695
     * Validate operands (arguments) of a function call operation and return
696
     * matching function
697
     *
698
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription[] $functions       List of functions to be checked
699
     * @param AbstractExpression[]  $argExpressions  Function argument expressions
700
     * @param ExpressionToken            $expressionToken Expression token
701
     *
702
     * @throws ODataException
703
     *
704
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription
705
     */
706 35
    public static function verifyFunctionCallOpArguments($functions,
707
        $argExpressions, $expressionToken
708
    ) {
709 35
        $function
710 35
            = self::findFunctionWithPromotion($functions, $argExpressions, false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type POData\Providers\Metadata\Type\Boolean expected by parameter $promoteArguments of POData\UriProcessor\Quer...FunctionWithPromotion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

710
            = self::findFunctionWithPromotion($functions, $argExpressions, /** @scrutinizer ignore-type */ false);
Loading history...
711 35
        if ($function == null) {
712 1
            $protoTypes = null;
713 1
            foreach ($functions as $function) {
714 1
                $protoTypes .= $function->getPrototypeAsString() . '; ';
715
            }
716
717 1
            throw ODataException::createSyntaxError(
718 1
                Messages::expressionLexerNoApplicableFunctionsFound(
719 1
                    $expressionToken->Text,
720 1
                    $protoTypes,
721 1
                    $expressionToken->Position
722 1
                )
723 1
            );
724
        }
725
726 35
        return $function;
727
    }
728
729
    /**
730
     * Finds a function from the list of functions whose argument types matches
731
     * with types of expressions
732
     *
733
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription[] $functionDescriptions List of functions
734
     * @param AbstractExpression[]  $argExpressions       Function argument expressions
735
     * @param Boolean                    $promoteArguments     Function argument
736
     *
737
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription|null Reference to the matching function if
738
     *                                  found else NULL
739
     */
740 51
    public static function findFunctionWithPromotion($functionDescriptions,
741
        $argExpressions, $promoteArguments = true
742
    ) {
743 51
        $argCount = count($argExpressions);
744 51
        $applicableFunctions = array();
745 51
        foreach ($functionDescriptions as $functionDescription) {
746 51
            if (count($functionDescription->argumentTypes) == $argCount) {
747 51
                $applicableFunctions[] = $functionDescription;
748
            }
749
        }
750
751 51
        if (empty($applicableFunctions)) {
752 1
            return null;
753
        }
754
755
        //Check for exact match
756 51
        foreach ($applicableFunctions as $function) {
757 51
            $i = 0;
758 51
            foreach ($function->argumentTypes as $argumentType) {
759 51
                if (!$argExpressions[$i]->typeIs($argumentType)) {
760 45
                    break;
761
                }
762
763 51
                $i++;
764
            }
765
766 51
            if ($i == $argCount) {
767 49
                return $function;
768
            }
769
        }
770
771
        //Check match with promotion
772 12
        $promotedTypes = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $promotedTypes is dead and can be removed.
Loading history...
773 12
        foreach ($applicableFunctions as $function) {
774 12
            $i = 0;
775 12
            $promotedTypes = array();
776 12
            foreach ($function->argumentTypes as $argumentType) {
777 12
                if (!$argumentType->isCompatibleWith($argExpressions[$i]->getType())) {
778 12
                    break;
779
                }
780
781 10
                $promotedTypes[] = $argumentType;
782 10
                $i++;
783
            }
784
785 12
            if ($i == $argCount) {
786 9
                $i = 0;
787 9
                if ($promoteArguments) {
788
                    //Promote Argument Expressions
789 3
                    foreach ($argExpressions as $expression) {
790 3
                        $expression->setType($promotedTypes[$i++]);
791
                    }
792
                }
793
794 9
                return $function;
795
            }
796
        }
797
798 5
        return null;
799
    }
800
}
801