FunctionDescription   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 751
Duplicated Lines 11.45 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 16
dl 86
loc 751
rs 1.0434
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getPrototypeAsString() 0 10 2
B filterFunctionDescriptions() 0 166 1
A stringComparisonFunctions() 0 9 1
A dateTimeComparisonFunctions() 0 9 1
A guidEqualityFunctions() 0 9 1
A binaryEqualityFunctions() 0 9 1
B arithmeticOperationFunctions() 0 29 1
A addOperationFunctions() 0 4 1
A subtractOperationFunctions() 0 4 1
A logicalOperationFunctions() 0 9 1
B relationalOperationFunctions() 0 24 1
A notOperationFunctions() 0 9 1
A isNullCheckFunction() 0 4 1
A negateOperationFunctions() 0 11 1
A incompatibleError() 0 20 3
A verifyAndPromoteArithmeticOpArguments() 16 16 2
A verifyLogicalOpArguments() 14 14 2
C verifyRelationalOpArguments() 40 69 14
B validateUnaryOpArguments() 16 22 5
A verifyFunctionExists() 0 15 2
A verifyFunctionCallOpArguments() 0 22 3
C findFunctionWithPromotion() 0 60 14

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 FunctionDescription 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 FunctionDescription, and based on these observations, apply Extract Interface, too.

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\Null1;
9
use POData\Providers\Metadata\Type\INavigationType;
10
use POData\Providers\Metadata\Type\Int64;
11
use POData\Providers\Metadata\Type\Int16;
12
use POData\Providers\Metadata\Type\Guid;
13
use POData\Providers\Metadata\Type\Single;
14
use POData\Providers\Metadata\Type\Double;
15
use POData\Providers\Metadata\Type\Decimal;
16
use POData\Providers\Metadata\Type\DateTime;
17
use POData\Providers\Metadata\Type\Int32;
18
use POData\Providers\Metadata\Type\StringType;
19
use POData\Providers\Metadata\Type\Boolean;
20
use POData\Providers\Metadata\Type\VoidType;
21
use POData\Providers\Metadata\Type\Binary;
22
use POData\Providers\Metadata\Type\IType;
23
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\ExpressionType;
24
use POData\UriProcessor\QueryProcessor\ExpressionParser\Expressions\AbstractExpression;
25
use POData\UriProcessor\QueryProcessor\ExpressionParser\ExpressionToken;
26
27
/**
28
 * Class FunctionDescription
29
 *
30
 * Class to represent function signature including function-name
31
 *
32
 * @package POData\UriProcessor\QueryProcessor\FunctionDescription
33
 */
34
class FunctionDescription
35
{
36
    /**
37
     * @var string
38
     */
39
    public $name;
40
41
    /**
42
     * @var IType
43
     */
44
    public $returnType;
45
46
    /**
47
     * @var IType[]
48
     */
49
    public $argumentTypes;
50
51
    /**
52
     * Create new instance of FunctionDescription
53
     * 
54
     * @param string  $name  Name of the function
55
     * @param IType   $returnType    Return type
56
     * @param IType[] $argumentTypes Parameter type
57
     */
58
    public function __construct($name, $returnType, $argumentTypes)
59
    {
60
        $this->name = $name;
61
        $this->returnType = $returnType;
62
        $this->argumentTypes = $argumentTypes;
63
    }
64
65
    /**      
66
     * Get the function prototype as string
67
     * 
68
     * @return string
69
     */
70
    public function getPrototypeAsString()
71
    {
72
        $str = $this->returnType->getFullTypeName() . ' ' . $this->name . '(';
73
74
	    foreach ($this->argumentTypes as $argumentType) {
75
            $str .= $argumentType->getFullTypeName() . ', ';
76
        }
77
78
        return rtrim($str, ', ') . ')';
79
    }
80
81
    /**      
82
     * Create function descriptions for supported function-calls in $filter option
83
     *
84
     * TODO: FIGURE OUT WHAT THE HECK THIS IS RETURNING!?!?
85
     *
86
     * @return array indexed by function name
87
     */
88
    public static function filterFunctionDescriptions()
89
    {
90
        $functions = array(
91
            //String Functions
92
            'endswith'      =>
93
                array(
94
                    new FunctionDescription(
95
                        'endswith', new Boolean(),
96
                        array(new StringType(), new StringType())
97
                    )
98
                ),
99
            'indexof'       =>
100
                array(
101
                    new FunctionDescription(
102
                        'indexof', new Int32(),
103
                        array(new StringType(), new StringType())
104
                    )
105
                ),
106
            'replace'       =>
107
                array(
108
                    new FunctionDescription(
109
                        'replace', new StringType(),
110
                        array(new StringType(), new StringType(), new StringType())
111
                    )
112
                ),
113
            'startswith'    =>
114
                array(
115
                    new FunctionDescription(
116
                        'startswith', new Boolean(),
117
                        array(new StringType(), new StringType())
118
                    )
119
                ),
120
            'tolower'       =>
121
                array(
122
                    new FunctionDescription(
123
                        'tolower', new StringType(),
124
                        array(new StringType())
125
                    )
126
                ),
127
            'toupper'       =>
128
                array(
129
                    new FunctionDescription(
130
                        'toupper', new StringType(),
131
                        array(new StringType())
132
                    )
133
                ),
134
            'trim'          =>
135
                array(
136
                    new FunctionDescription(
137
                        'trim', new StringType(),
138
                        array(new StringType())
139
                    )
140
                ),
141
            'substring'     =>
142
                array(
143
                    new FunctionDescription(
144
                        'substring', new StringType(),
145
                        array(new StringType(), new Int32())
146
                    ),
147
                    new FunctionDescription(
148
                        'substring', new StringType(),
149
                        array(new StringType(), new Int32(), new Int32())
150
                    )
151
                ),
152
            'substringof'   =>
153
                array(
154
                    new FunctionDescription(
155
                        'substringof', new Boolean(),
156
                        array(new StringType(), new StringType())
157
                    )
158
                ),
159
            'concat'        =>
160
                array(
161
                    new FunctionDescription(
162
                        'concat', new StringType(),
163
                        array(new StringType(), new StringType())
164
                    )
165
                ),
166
            'length'        =>
167
                array(
168
                    new FunctionDescription(
169
                        'length', new Int32(),
170
                        array(new StringType())
171
                    )
172
                ),
173
            //DateTime functions
174
            'year'          =>
175
                array(
176
                    new FunctionDescription(
177
                        'year', new Int32(),
178
                        array(new DateTime())
179
                    )
180
                ),
181
            'month'         =>
182
                array(
183
                    new FunctionDescription(
184
                        'month', new Int32(),
185
                        array(new DateTime())
186
                    )
187
                ),
188
            'day'           =>
189
                array(
190
                    new FunctionDescription(
191
                        'day', new Int32(),
192
                        array(new DateTime())
193
                    )
194
                ),
195
            'hour'          =>
196
                array(
197
                    new FunctionDescription(
198
                        'hour', new Int32(),
199
                        array(new DateTime())
200
                    )
201
                ),
202
            'minute'        =>
203
                array(
204
                    new FunctionDescription(
205
                        'minute', new Int32(),
206
                        array(new DateTime())
207
                    )
208
                ),
209
            'second'        =>
210
                array(
211
                    new FunctionDescription(
212
                        'second', new Int32(),
213
                        array(new DateTime())
214
                    )
215
                ),
216
            //Math Functions
217
            'round'         =>
218
                array(
219
                    new FunctionDescription(
220
                        'round', new Decimal(),
221
                        array(new Decimal())
222
                    ),
223
                    new FunctionDescription(
224
                        'round', new Double(),
225
                        array(new Double())
226
                    )
227
                ),
228
            'ceiling'       =>
229
                array(
230
                    new FunctionDescription(
231
                        'ceiling', new Decimal(),
232
                        array(new Decimal())
233
                    ),
234
                    new FunctionDescription(
235
                        'ceiling', new Double(),
236
                        array(new Double())
237
                    )
238
                ),
239
            'floor'         =>
240
                array(
241
                    new FunctionDescription(
242
                        'floor', new Decimal(),
243
                        array(new Decimal())
244
                    ),
245
                    new FunctionDescription(
246
                        'floor', new Double(),
247
                        array(new Double())
248
                    )
249
                )
250
          );
251
252
        return $functions;
253
    }
254
255
    /** 
256
     * Get function description for string comparison
257
     * 
258
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription[]
259
     */
260
    public static function stringComparisonFunctions()
261
    {
262
        return array(
263
            new FunctionDescription(
264
                'strcmp', new Int32(), 
265
                array(new StringType(), new StringType())
266
            )
267
        );
268
    }
269
270
    /**
271
     * Get function description for datetime comparison
272
     * 
273
     * @return \POData\UriProcessor\QueryProcessor\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 \POData\UriProcessor\QueryProcessor\FunctionDescription[]
289
     */
290
    public static function guidEqualityFunctions()
291
    {
292
        return array(
293
            new FunctionDescription(
294
                'guidEqual', new Boolean(), 
295
                array(new Guid(), new Guid())
296
            )
297
        );
298
    }
299
    
300
    /**
301
     * Get function description for binary equality check
302
     * 
303
     * @return \POData\UriProcessor\QueryProcessor\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 \POData\UriProcessor\QueryProcessor\FunctionDescription[]
319
     */
320
    public static function arithmeticOperationFunctions()
321
    {      
322
        return array(
323
            new FunctionDescription(
324
                'F', new int16(), 
325
                array(new int16(), new int16())
326
            ),
327
            new FunctionDescription(
328
                'F', new int32(), 
329
                array(new int32(), new int32())
330
            ),
331
            new FunctionDescription(
332
                'F', new int64(), 
333
                array(new int64(), new int64())
334
            ),
335
            new FunctionDescription(
336
                'F', new Single(), 
337
                array(new Single(), new Single())
338
            ),
339
            new FunctionDescription(
340
                'F', new Double(), 
341
                array(new Double(), new Double())
342
            ),
343
            new FunctionDescription(
344
                'F', new Decimal(), 
345
                array(new Decimal(), new Decimal())
346
            )
347
        );
348
    }
349
350
    /**      
351
     * Get function descriptions for arithmetic add operations
352
     * 
353
     * @return \POData\UriProcessor\QueryProcessor\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 \POData\UriProcessor\QueryProcessor\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 \POData\UriProcessor\QueryProcessor\FunctionDescription[]
374
     */
375
    public static function logicalOperationFunctions()
376
    {
377
        return array(
378
            new FunctionDescription(
379
                'F', new Boolean(), 
380
                array(new Boolean(), new Boolean())
381
            )
382
        );
383
    }
384
385
    /**
386
     * Get function descriptions for relational operations
387
     * 
388
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription[]
389
     */
390
    public static function relationalOperationFunctions()
391
    {
392
        return array_merge(
393
            self::arithmeticOperationFunctions(),
394
            array(
395
                new FunctionDescription(
396
                    'F', new Boolean(), 
397
                    array(new Boolean(), new Boolean())
398
                ),
399
                new FunctionDescription(
400
                    'F', new DateTime(), 
401
                    array(new DateTime(), new DateTime())
402
                ),
403
                new FunctionDescription(
404
                    'F', new Guid(), 
405
                    array(new Guid(), new Guid())
406
                ),
407
                new FunctionDescription(
408
                    'F', new Boolean(), 
409
                    array(new Binary(), new Binary())
410
                )
411
            )
412
        );
413
    }
414
415
    /**
416
     * Get function descriptions for unary not operation
417
     * 
418
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription[]
419
     */
420
    public static function notOperationFunctions()
421
    {
422
        return array(
423
            new FunctionDescription(
424
                'F', new Boolean(), 
425
                array(new Boolean())
426
            )
427
        );
428
    }
429
430
    /**
431
     * Get function description for checking an operand is null or not
432
     * 
433
     * @param IType $type Type of the argument to null check function.
434
     * 
435
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription
436
     */
437
    public static function isNullCheckFunction(IType $type)
438
    {
439
        return new FunctionDescription('is_null', new Boolean(), array($type));
440
    }
441
    
442
    /**      
443
     * Get function description for unary negate operator
444
     * 
445
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription[]
446
     */
447
    public static function negateOperationFunctions()
448
    {
449
        return array(
450
            new FunctionDescription('F', new Int16(), array(new Int16())),
451
            new FunctionDescription('F', new Int32(), array(new Int32())),
452
            new FunctionDescription('F', new Int64(), array(new Int64())),
453
            new FunctionDescription('F', new Single(), array(new Single())),
454
            new FunctionDescription('F', new Double(), array(new Double())),
455
            new FunctionDescription('F', new Decimal(), array(new Decimal()))
456
        );
457
    }
458
459
    /**
460
     * To throw ODataException for incompatible types
461
     * 
462
     * @param ExpressionToken           $expressionToken Expression token
463
     * @param AbstractExpression[] $argExpressions  Array of argument expression
464
     * 
465
     * @throws ODataException
466
     * @return void
467
     */
468
    public static function incompatibleError($expressionToken, $argExpressions)
469
    {
470
        $string = null;
471
        foreach ($argExpressions as $argExpression) {
472
            $string .= $argExpression->getType()->getFullTypeName() . ', ';
473
        }
474
475
        $string = rtrim($string, ', ');
476
        $pos = strrpos($string, ', ');
477
        if ($pos !== false) {
478
            $string = substr_replace($string, ' and ', strrpos($string, ', '), 2);
479
        }
480
481
        throw ODataException::createSyntaxError(
482
            Messages::expressionParserInCompatibleTypes(
483
                $expressionToken->Text, 
484
                $string, $expressionToken->Position
485
            )
486
        );
487
    }
488
489
    /**      
490
     * Validate operands of an arithmetic operation and promote if required
491
     * 
492
     * @param ExpressionToken    $expressionToken The expression token
493
     * @param AbstractExpression $leftArgument    The left expression
494
     * @param AbstractExpression $rightArgument   The right expression
495
     * 
496
     * @return IType
497
     */
498 View Code Duplication
    public static function verifyAndPromoteArithmeticOpArguments($expressionToken, 
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...
499
        $leftArgument, $rightArgument
500
    ) {
501
        $function  
502
            = self::findFunctionWithPromotion(
503
                self::arithmeticOperationFunctions(),
504
                array($leftArgument, $rightArgument)
505
            );
506
        if ($function == null) {
507
            self::incompatibleError(
508
                $expressionToken, array($leftArgument, $rightArgument)
509
            );
510
        }
511
512
        return $function->returnType;
513
    }
514
    
515
    /**      
516
     * Validate operands of an logical operation
517
     * 
518
     * @param ExpressionToken    $expressionToken The expression token
519
     * @param AbstractExpression $leftArgument    The left expression
520
     * @param AbstractExpression $rightArgument   The right expression
521
     * 
522
     * @return void
523
     * 
524
     * @throws ODataException
525
     */
526 View Code Duplication
    public static function verifyLogicalOpArguments($expressionToken, 
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...
527
        $leftArgument, $rightArgument
528
    ) {
529
        $function = self::findFunctionWithPromotion(
530
            self::logicalOperationFunctions(), 
531
            array($leftArgument, $rightArgument), false
532
        );
533
        if ($function == null) {
534
            self::incompatibleError(
535
                $expressionToken, 
536
                array($leftArgument, $rightArgument)
537
            );
538
        }
539
    }
540
541
    /**
542
     * Validate operands of an relational operation
543
     * 
544
     * @param ExpressionToken    $expressionToken The expression token
545
     * @param AbstractExpression $leftArgument    The left argument expression
546
     * @param AbstractExpression $rightArgument   The right argument expression
547
     * 
548
     * @return void
549
     */
550
    public static function verifyRelationalOpArguments($expressionToken, 
551
        $leftArgument, $rightArgument
552
    ) {
553
        //for null operands only equality operators are allowed
554
        $null = new Null1();
555 View Code Duplication
        if ($leftArgument->typeIs($null) || $rightArgument->typeIs($null)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
556
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0) 
557
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
558
            ) {                
559
                throw ODataException::createSyntaxError(
560
                    Messages::expressionParserOperatorNotSupportNull(
561
                        $expressionToken->Text, 
562
                        $expressionToken->Position
563
                    )
564
                );
565
            }
566
567
            return;
568
        }
569
570
        //for guid operands only equality operators are allowed
571
        $guid = new Guid();
572 View Code Duplication
        if ($leftArgument->typeIs($guid) && $rightArgument->typeIs($guid)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
573
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0) 
574
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
575
            ) {                
576
                throw ODataException::createSyntaxError(
577
                    Messages::expressionParserOperatorNotSupportGuid(
578
                        $expressionToken->Text, $expressionToken->Position
579
                    )
580
                );
581
            }
582
583
            return;
584
        }       
585
        
586
        //for binary operands only equality operators are allowed
587
        $binary = new Binary();
588 View Code Duplication
        if ($leftArgument->typeIs($binary) && $rightArgument->typeIs($binary)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
589
            if ((strcmp($expressionToken->Text, ODataConstants::KEYWORD_EQUAL) != 0) 
590
                && (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT_EQUAL) != 0)
591
            ) {                
592
                throw ODataException::createSyntaxError(
593
                    Messages::expressionParserOperatorNotSupportBinary(
594
                        $expressionToken->Text, $expressionToken->Position
595
                    )
596
                );
597
            }
598
599
            return;
600
        }
601
        
602
        //TODO: eq and ne is valid for 'resource reference' 
603
        //navigation also verify here
604
605
        $functions = array_merge(
606
            self::relationalOperationFunctions(), 
607
            self::stringComparisonFunctions()
608
        );
609
        $function = self::findFunctionWithPromotion(
610
            $functions, array($leftArgument, $rightArgument), false
611
        );
612
        if ($function == null) {
613
            self::incompatibleError(
614
                $expressionToken, 
615
                array($leftArgument, $rightArgument)
616
            );
617
        }
618
    }
619
620
    /**
621
     * Validate operands of a unary  operation
622
     * 
623
     * @param ExpressionToken    $expressionToken The expression token
624
     * @param AbstractExpression $argExpression   Argument expression
625
     * 
626
     * @throws ODataException
627
     * 
628
     * @return void
629
     */
630
    public static function validateUnaryOpArguments($expressionToken, $argExpression)
631
    {
632
        //Unary not
633 View Code Duplication
        if (strcmp($expressionToken->Text, ODataConstants::KEYWORD_NOT) == 0 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
634
            $function = self::findFunctionWithPromotion(
635
                self::notOperationFunctions(), 
636
                array($argExpression)
637
            );
638
            if ($function == null) {
639
                self::incompatibleError($expressionToken, array($argExpression));
640
            }
641
642
            return;
643
        }
644
645
        //Unary minus (negation)
646 View Code Duplication
        if (strcmp($expressionToken->Text, '-') == 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
647
            if (self::findFunctionWithPromotion(self::negateOperationFunctions(), array($argExpression)) == null) {
648
                self::incompatibleError($expressionToken, array($argExpression));
649
            }
650
        }
651
    }
652
    
653
    /**
654
     * Check am identifier is a valid filter function
655
     * 
656
     * @param ExpressionToken $expressionToken The expression token      
657
     * 
658
     * @throws ODataException
659
     * 
660
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription[] Array of matching functions
661
     */
662
    public static function verifyFunctionExists($expressionToken)
663
    {
664
        if (!array_key_exists($expressionToken->Text, self::filterFunctionDescriptions())) {
665
            throw ODataException::createSyntaxError(
666
                Messages::expressionParserUnknownFunction(
667
                    $expressionToken->Text, 
668
                    $expressionToken->Position
669
                )
670
            );
671
            
672
        }
673
674
        $filterFunctions =  self::filterFunctionDescriptions();
675
        return $filterFunctions[$expressionToken->Text];
676
    }
677
678
    /**
679
     * Validate operands (arguments) of a function call operation and return 
680
     * matching function
681
     * 
682
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription[] $functions       List of functions to be checked
683
     * @param AbstractExpression[]  $argExpressions  Function argument expressions
684
     * @param ExpressionToken            $expressionToken Expression token
685
     * 
686
     * @throws ODataException
687
     * 
688
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription
689
     */
690
    public static function verifyFunctionCallOpArguments($functions, 
691
        $argExpressions, $expressionToken
692
    ) {
693
        $function 
694
            = self::findFunctionWithPromotion($functions, $argExpressions, false);
695
        if ($function == null) {
696
            $protoTypes = null;
697
            foreach ($functions as $function) {
698
                $protoTypes .=  $function->getPrototypeAsString() . '; ';
699
            }
700
701
            throw ODataException::createSyntaxError(
702
                Messages::expressionLexerNoApplicableFunctionsFound(
703
                    $expressionToken->Text, 
704
                    $protoTypes, 
705
                    $expressionToken->Position
706
                )
707
            );
708
        }
709
710
        return $function;
711
    }
712
713
    /**
714
     * Finds a function from the list of functions whose argument types matches 
715
     * with types of expressions
716
     * 
717
     * @param \POData\UriProcessor\QueryProcessor\FunctionDescription[] $functionDescriptions List of functions
718
     * @param AbstractExpression[]  $argExpressions       Function argument expressions
719
     * @param Boolean                    $promoteArguments     Function argument
720
     * 
721
     * @return \POData\UriProcessor\QueryProcessor\FunctionDescription|null Reference to the matching function if
722
     *                                  found else NULL
723
     */
724
    public static function findFunctionWithPromotion($functionDescriptions, 
725
        $argExpressions, $promoteArguments = true
726
    ) {
727
        $argCount = count($argExpressions);
728
        $applicableFunctions = array();
729
        foreach ($functionDescriptions as $functionDescription) {
730
            if (count($functionDescription->argumentTypes) == $argCount) {
731
                $applicableFunctions[] = $functionDescription;
732
            }
733
        }
734
735
        if (empty($applicableFunctions)) {
736
            return null;
737
        }
738
739
        //Check for exact match
740
        foreach ($applicableFunctions as $function) {
741
            $i = 0;
742
            foreach ($function->argumentTypes as $argumentType) {
743
                if (!$argExpressions[$i]->typeIs($argumentType)) {
744
                    break;
745
                }
746
747
                $i++;
748
            }
749
750
            if ($i == $argCount) {
751
                return $function;
752
            }
753
        }
754
755
        //Check match with promotion
756
        $promotedTypes = array();
0 ignored issues
show
Unused Code introduced by
$promotedTypes 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...
757
        foreach ($applicableFunctions as $function) {
758
            $i = 0;
759
            $promotedTypes = array();
760
            foreach ($function->argumentTypes as $argumentType) {
761
                if (!$argumentType->isCompatibleWith($argExpressions[$i]->getType())) {
762
                    break;
763
                }
764
765
                $promotedTypes[] = $argumentType;
766
                $i++;
767
            }
768
769
            if ($i == $argCount) {
770
                $i = 0;
771
                if ($promoteArguments) {
772
                    //Promote Argument Expressions
773
                    foreach ($argExpressions as $expression) {
774
                        $expression->setType($promotedTypes[$i++]);
775
                    }
776
                }
777
778
                return $function;
779
            }
780
        }
781
782
        return null;
783
    }
784
}