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