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, |
|
|
|
|
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, |
|
|
|
|
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)) { |
|
|
|
|
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)) { |
|
|
|
|
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)) { |
|
|
|
|
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 ) { |
|
|
|
|
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) { |
|
|
|
|
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(); |
|
|
|
|
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
|
|
|
} |
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.