GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

PhpTypeSystem   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 534
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 18

Importance

Changes 0
Metric Value
wmc 61
lcom 2
cbo 18
dl 0
loc 534
rs 2.9203
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A typeDataModules() 0 9 1
A getTypeDataModules() 0 4 1
A registerTypeDataModule() 0 15 3
A normalizeClassName() 0 8 2
A normalizeFunctionName() 0 8 2
A buildFunction() 0 7 2
A buildCompositeType() 0 7 1
A getCommonAncestorType() 0 18 4
A getTypeFromValue() 0 4 1
B getTypeFromTypeHint() 0 14 5
A nativeCasts() 0 11 1
A nativeType() 0 18 1
A commonNativeUnaryOperations() 0 8 1
B nativeTypes() 0 83 1
A getAncestorTypes() 0 20 4
B getObjectTypeData() 0 19 6
F buildObjectType() 0 76 15
A objectCasts() 0 7 1
A objectUnaryOperations() 0 10 1
A booleanOperator() 0 4 1
B mathOperators() 0 40 2
B bitwiseOperators() 0 28 2
B binaryOperations() 0 42 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Pinq\Analysis;
4
5
use Pinq;
6
use Pinq\Analysis\Functions\Func;
7
use Pinq\Analysis\TypeData\ITypeDataModule;
8
use Pinq\Analysis\TypeOperations\Constructor;
9
use Pinq\Analysis\TypeOperations\Field;
10
use Pinq\Analysis\TypeOperations\Indexer;
11
use Pinq\Analysis\TypeOperations\Method;
12
use Pinq\Analysis\Types\CompositeType;
13
use Pinq\Analysis\Types\MixedType;
14
use Pinq\Analysis\Types\NativeType;
15
use Pinq\Analysis\Types\ObjectType;
16
use Pinq\Expressions\Operators;
17
18
/**
19
 * Default implementation of the type system representing a subset
20
 * of PHP's excuse of a type system.
21
 *
22
 * @author Elliot Levin <[email protected]>
23
 */
24
class PhpTypeSystem extends TypeSystem
25
{
26
    const TYPE_SELF = '~~SELF_TYPE~~';
27
28
    /**
29
     * @var ITypeDataModule[]
30
     */
31
    protected $typeDataModules = [];
32
33
    /**
34
     * @var string[]
35
     */
36
    protected $functionTypeMap = [];
37
38
    /**
39
     * @var array[]
40
     */
41
    protected $classTypeMap = [];
42
43
    /**
44
     * @param ITypeDataModule[] $customTypeDataModules
45
     */
46
    public function __construct(array $customTypeDataModules = [])
47
    {
48
        parent::__construct();
49
50
        $typeDataModules = array_merge($this->typeDataModules(), $customTypeDataModules);
51
        /** @var $typeDataModules ITypeDataModule[] */
52
        foreach ($typeDataModules as $module) {
53
            $this->registerTypeDataModule($module);
54
        }
55
    }
56
57
    /**
58
     * @return ITypeDataModule[]
59
     */
60
    protected function typeDataModules()
61
    {
62
        return [
63
                new TypeData\InternalFunctions(),
64
                new TypeData\InternalTypes(),
65
                new TypeData\DateTime(),
66
                new TypeData\PinqAPI(),
67
        ];
68
    }
69
70
    /**
71
     * Gets the type data modules from the type system.
72
     *
73
     * @return ITypeDataModule[]
74
     */
75
    public function getTypeDataModules()
76
    {
77
        return $this->typeDataModules;
78
    }
79
80
    /**
81
     * Adds the type data module to the type system.
82
     *
83
     * @param ITypeDataModule $module
84
     *
85
     * @return void
86
     */
87
    public function registerTypeDataModule(ITypeDataModule $module)
88
    {
89
        $this->typeDataModules[] = $module;
90
        foreach ($module->functions() as $name => $returnType) {
91
            $normalizedFunctionName                         = $this->normalizeFunctionName($name);
92
            $this->functionTypeMap[$normalizedFunctionName] = $returnType;
93
            unset($this->functions[$normalizedFunctionName]);
94
        }
95
96
        foreach ($module->types() as $name => $typeData) {
97
            $normalizedClassName                      = $this->normalizeClassName($name);
98
            $this->classTypeMap[$normalizedClassName] = $typeData;
99
            unset($this->objectTypes[$normalizedClassName]);
100
        }
101
    }
102
103
    // Normalize function / type names using reflection to get originally defined name
104
    // but fallback to lower casing due to some functions that are not universally available
105
    // such as 'money_format' which will fail with reflection if not available.
106
    protected function normalizeClassName($name)
107
    {
108
        try {
109
            return (new \ReflectionClass($name))->getName();
0 ignored issues
show
Bug introduced by
Consider using (new \ReflectionClass($name))->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
110
        } catch (\Exception $exception) {
111
            return strtolower($name);
112
        }
113
    }
114
115
    protected function normalizeFunctionName($name)
116
    {
117
        try {
118
            return (new \ReflectionFunction($name))->getName();
0 ignored issues
show
Bug introduced by
Consider using (new \ReflectionFunction($name))->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
119
        } catch (\Exception $exception) {
120
            return strtolower($name);
121
        }
122
    }
123
124
    protected function buildFunction($name)
125
    {
126
        return new Func(
127
                $this,
128
                $name,
129
                isset($this->functionTypeMap[$name]) ? $this->functionTypeMap[$name] : INativeType::TYPE_MIXED);
130
    }
131
132
    protected function buildCompositeType($typeId, array $types)
133
    {
134
        return new CompositeType(
135
                $typeId,
136
                $this->nativeTypes[INativeType::TYPE_MIXED],
137
                $types);
138
    }
139
140
    public function getCommonAncestorType(IType $type, IType $otherType)
141
    {
142
        if ($type->isEqualTo($otherType)) {
143
            return $type;
144
        } elseif ($type->isParentTypeOf($otherType)) {
145
            return $type;
146
        } elseif ($otherType->isParentTypeOf($type)) {
147
            return $otherType;
148
        }
149
150
        $parentTypes      = $this->getAncestorTypes($type);
151
        $otherParentTypes = $this->getAncestorTypes($otherType);
152
153
        /** @var $commonParentTypes IType[] */
154
        $commonParentTypes = array_intersect_key($parentTypes, $otherParentTypes);
155
156
        return $this->getCompositeType($commonParentTypes);
157
    }
158
159
    public function getTypeFromValue($value)
160
    {
161
        return $this->getType(TypeId::fromValue($value));
162
    }
163
164
    public function getTypeFromTypeHint($typeHint)
165
    {
166
        if ($typeHint === null || $typeHint === '') {
167
            return $this->nativeTypes[INativeType::TYPE_MIXED];
168
        }
169
170
        if (strcasecmp($typeHint, 'callable') === 0) {
171
            return $this->nativeTypes[INativeType::TYPE_MIXED];
172
        } elseif (strcasecmp($typeHint, 'array') === 0) {
173
            return $this->nativeTypes[INativeType::TYPE_ARRAY];
174
        } else {
175
            return $this->getObjectType($typeHint);
176
        }
177
    }
178
179
    protected function nativeCasts()
180
    {
181
        return [
182
                Operators\Cast::STRING     => INativeType::TYPE_STRING,
183
                Operators\Cast::BOOLEAN    => INativeType::TYPE_BOOL,
184
                Operators\Cast::INTEGER    => INativeType::TYPE_INT,
185
                Operators\Cast::DOUBLE     => INativeType::TYPE_DOUBLE,
186
                Operators\Cast::ARRAY_CAST => INativeType::TYPE_ARRAY,
187
                Operators\Cast::OBJECT     => TypeId::getObject('stdClass'),
188
        ];
189
    }
190
191
    protected function nativeType(
192
            $typeOfType,
193
            IType $parentType,
194
            IIndexer $indexer = null,
195
            array $unaryOperatorMap = [],
196
            array $castMap = []
197
    ) {
198
        return new NativeType(
199
                $typeOfType,
200
                $parentType,
201
                $typeOfType,
202
                $indexer,
203
                $this->buildTypeOperations($typeOfType, array_filter($castMap + $this->nativeCasts())),
204
                $this->buildTypeOperations(
205
                        $typeOfType,
206
                        array_filter($unaryOperatorMap + $this->commonNativeUnaryOperations())
207
                ));
208
    }
209
210
    protected function commonNativeUnaryOperations()
211
    {
212
        return [
213
                Operators\Unary::NOT      => INativeType::TYPE_BOOL,
214
                Operators\Unary::PLUS     => INativeType::TYPE_INT,
215
                Operators\Unary::NEGATION => INativeType::TYPE_INT,
216
        ];
217
    }
218
219
    protected function nativeTypes()
220
    {
221
        return [
222
                $mixedType = new MixedType(INativeType::TYPE_MIXED),
223
                $numericType = $this->nativeType(
224
                        INativeType::TYPE_NUMERIC,
225
                        $mixedType,
226
                        null,
227
                        [
228
                                Operators\Unary::BITWISE_NOT   => INativeType::TYPE_INT,
229
                                Operators\Unary::PLUS          => INativeType::TYPE_NUMERIC,
230
                                Operators\Unary::NEGATION      => INativeType::TYPE_NUMERIC,
231
                                Operators\Unary::INCREMENT     => INativeType::TYPE_NUMERIC,
232
                                Operators\Unary::DECREMENT     => INativeType::TYPE_NUMERIC,
233
                                Operators\Unary::PRE_INCREMENT => INativeType::TYPE_NUMERIC,
234
                                Operators\Unary::PRE_DECREMENT => INativeType::TYPE_NUMERIC,
235
                        ]
236
                ),
237
                $this->nativeType(
238
                        INativeType::TYPE_STRING,
239
                        $mixedType,
240
                        new Indexer($this, INativeType::TYPE_STRING, INativeType::TYPE_STRING),
241
                        [
242
                                Operators\Unary::BITWISE_NOT   => INativeType::TYPE_STRING,
243
                                Operators\Unary::INCREMENT     => INativeType::TYPE_STRING,
244
                                Operators\Unary::DECREMENT     => INativeType::TYPE_STRING,
245
                                Operators\Unary::PRE_INCREMENT => INativeType::TYPE_MIXED,
246
                                Operators\Unary::PRE_DECREMENT => INativeType::TYPE_MIXED,
247
                        ]
248
                ),
249
                $this->nativeType(
250
                        INativeType::TYPE_ARRAY,
251
                        $mixedType,
252
                        new Indexer($this, INativeType::TYPE_ARRAY, INativeType::TYPE_MIXED),
253
                        [
254
                                Operators\Unary::PLUS     => null,
255
                                Operators\Unary::NEGATION => null,
256
                        ],
257
                        [
258
                                Operators\Cast::STRING => null,
259
                        ]
260
                ),
261
                $this->nativeType(
262
                        INativeType::TYPE_INT,
263
                        $numericType,
264
                        null,
265
                        [
266
                                Operators\Unary::BITWISE_NOT   => INativeType::TYPE_INT,
267
                                Operators\Unary::INCREMENT     => INativeType::TYPE_INT,
268
                                Operators\Unary::DECREMENT     => INativeType::TYPE_INT,
269
                                Operators\Unary::PRE_INCREMENT => INativeType::TYPE_INT,
270
                                Operators\Unary::PRE_DECREMENT => INativeType::TYPE_INT,
271
                        ]
272
                ),
273
                $this->nativeType(
274
                        INativeType::TYPE_DOUBLE,
275
                        $numericType,
276
                        null,
277
                        [
278
                                Operators\Unary::BITWISE_NOT   => INativeType::TYPE_INT,
279
                                Operators\Unary::PLUS          => INativeType::TYPE_DOUBLE,
280
                                Operators\Unary::NEGATION      => INativeType::TYPE_DOUBLE,
281
                                Operators\Unary::INCREMENT     => INativeType::TYPE_DOUBLE,
282
                                Operators\Unary::DECREMENT     => INativeType::TYPE_DOUBLE,
283
                                Operators\Unary::PRE_INCREMENT => INativeType::TYPE_DOUBLE,
284
                                Operators\Unary::PRE_DECREMENT => INativeType::TYPE_DOUBLE,
285
                        ]
286
                ),
287
                $this->nativeType(
288
                        INativeType::TYPE_BOOL,
289
                        $mixedType,
290
                        null,
291
                        [
292
                                Operators\Unary::INCREMENT     => INativeType::TYPE_BOOL,
293
                                Operators\Unary::DECREMENT     => INativeType::TYPE_BOOL,
294
                                Operators\Unary::PRE_INCREMENT => INativeType::TYPE_BOOL,
295
                                Operators\Unary::PRE_DECREMENT => INativeType::TYPE_BOOL,
296
                        ]
297
                ),
298
                $this->nativeType(INativeType::TYPE_NULL, $mixedType),
299
                $this->nativeType(INativeType::TYPE_RESOURCE, $mixedType),
300
        ];
301
    }
302
303
    protected function getAncestorTypes(IType $type)
304
    {
305
        $ancestorTypes = [$type->getIdentifier() => $type];
306
307
        if (!$type->hasParentType()) {
308
            return $ancestorTypes;
309
        }
310
311
        if ($type instanceof ICompositeType) {
312
            foreach ($type->getComposedTypes() as $composedType) {
313
                $ancestorTypes += $this->getAncestorTypes($composedType);
314
            }
315
        } else {
316
            $parentType                                  = $type->getParentType();
317
            $ancestorTypes[$parentType->getIdentifier()] = $parentType;
318
            $ancestorTypes += $this->getAncestorTypes($parentType);
0 ignored issues
show
Bug introduced by
It seems like $parentType defined by $type->getParentType() on line 316 can be null; however, Pinq\Analysis\PhpTypeSystem::getAncestorTypes() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
319
        }
320
321
        return $ancestorTypes;
322
    }
323
324
    protected function getObjectTypeData($classType)
325
    {
326
        $classType = $this->normalizeClassName($classType);
327
        $data      = isset($this->classTypeMap[$classType]) ? $this->classTypeMap[$classType] : [];
328
329
        foreach (['methods', 'fields', 'static-fields'] as $property) {
330
            if (!isset($data[$property])) {
331
                $data[$property] = [];
332
            }
333
334
            foreach ($data[$property] as &$returnType) {
335
                if ($returnType === self::TYPE_SELF) {
336
                    $returnType = TypeId::getObject($classType);
337
                }
338
            }
339
        }
340
341
        return $data;
342
    }
343
344
    protected function buildObjectType($typeId, $classType)
345
    {
346
        $typeData            = $this->getObjectTypeData($classType);
347
        $methodReturnTypeMap = $typeData['methods'];
348
        $fieldTypeMap        = $typeData['fields'];
349
        $staticFieldTypeMap  = $typeData['static-fields'];
350
351
        $reflection      = new \ReflectionClass($classType);
352
        $constructor     = new Constructor($this, $typeId, $reflection->getConstructor());
353
        $methods         = [];
354
        $fields          = [];
355
        $indexer         = null;
356
        $invoker         = null;
357
        $unaryOperations = $this->buildTypeOperations($this->objectUnaryOperations($typeId));
358
        $casts           = $this->buildTypeOperations($this->objectCasts($typeId));
359
360
        $parentTypes = array_map([$this, 'getObjectType'], $reflection->getInterfaceNames());
361
        if ($parentClass = $reflection->getParentClass()) {
362
            $parentTypes[] = $this->getObjectType($parentClass->getName());
363
        }
364
365
        $parentType      = $this->getCompositeType($parentTypes);
366
367
        if ($reflection->hasMethod('__toString')) {
368
            $methodReturnTypeMap += ['__toString' => INativeType::TYPE_STRING];
369
        }
370
371
        foreach ($methodReturnTypeMap as $name => $type) {
372
            $methods[$name] = new Method($this, $typeId, $reflection->getMethod($name), $type);
373
        }
374
375
        foreach ($reflection->getMethods() as $method) {
376
            if ($method->getDeclaringClass()->getName() === $classType
0 ignored issues
show
introduced by
Consider using $method->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
377
                    && !isset($methods[$method->getName()])
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
378
            ) {
379
                $methods[$method->getName()] = new Method($this, $typeId, $method, INativeType::TYPE_MIXED);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
380
            }
381
        }
382
383
        foreach ($staticFieldTypeMap + $fieldTypeMap as $name => $type) {
384
            $fields[$name] = new Field($this, $typeId, $name, isset($staticFieldTypeMap[$name]), $type);
385
        }
386
387
        foreach ($reflection->getProperties() as $field) {
388
            if ($field->getDeclaringClass()->getName() === $classType
0 ignored issues
show
introduced by
Consider using $field->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
389
                    && !isset($fields[$field->getName()])
390
            ) {
391
                $fields[$field->getName()] = new Field($this, $typeId, $field->getName(), $field->isStatic(
392
                ), INativeType::TYPE_MIXED);
393
            }
394
        }
395
396
        if ($reflection->implementsInterface('ArrayAccess') && isset($methods['offsetGet'])) {
397
            $indexer = $methods['offsetGet'];
398
        }
399
400
        if (isset($methods['__invoke'])) {
401
            $invoker = $methods['__invoke'];
402
        }
403
404
        if (isset($methods['__toString'])) {
405
            $casts[Operators\Cast::STRING] = $methods['__toString'];
406
        }
407
408
        return new ObjectType(
409
                $typeId,
410
                $reflection,
411
                $parentType,
412
                $constructor,
413
                $methods,
414
                $fields,
415
                $unaryOperations,
416
                $casts,
417
                $invoker,
418
                $indexer);
419
    }
420
421
    protected function objectCasts($objectTypeId)
422
    {
423
        return [
424
                Operators\Cast::ARRAY_CAST => INativeType::TYPE_ARRAY,
425
                Operators\Cast::OBJECT     => $objectTypeId,
426
        ];
427
    }
428
429
    protected function objectUnaryOperations($objectTypeId)
430
    {
431
        return [
432
                Operators\Unary::NOT           => INativeType::TYPE_BOOL,
433
                Operators\Unary::INCREMENT     => $objectTypeId,
434
                Operators\Unary::DECREMENT     => $objectTypeId,
435
                Operators\Unary::PRE_INCREMENT => $objectTypeId,
436
                Operators\Unary::PRE_DECREMENT => $objectTypeId,
437
        ];
438
    }
439
440
    protected function booleanOperator($operator)
441
    {
442
        return [INativeType::TYPE_MIXED, $operator, INativeType::TYPE_MIXED, 'return' => INativeType::TYPE_BOOL];
443
    }
444
445
    protected function mathOperators($operator, $otherIntReturnType = INativeType::TYPE_INT)
446
    {
447
        //TODO: remove duplicate operators with types on opposite sides (binary operators are symmetrical)
448
        $operators = [];
449
        foreach ([
450
                         INativeType::TYPE_INT,
451
                         INativeType::TYPE_DOUBLE,
452
                         INativeType::TYPE_NUMERIC,
453
                         INativeType::TYPE_STRING,
454
                         INativeType::TYPE_RESOURCE,
455
                         INativeType::TYPE_BOOL,
456
                         INativeType::TYPE_NULL
457
                 ] as $type) {
458
            $operators = array_merge(
459
                    $operators,
460
                    [
461
                            [$type, $operator, INativeType::TYPE_NULL, 'return' => $otherIntReturnType],
462
                            [$type, $operator, INativeType::TYPE_BOOL, 'return' => $otherIntReturnType],
463
                            [$type, $operator, INativeType::TYPE_STRING, 'return' => INativeType::TYPE_NUMERIC],
464
                            [$type, $operator, INativeType::TYPE_RESOURCE, 'return' => $otherIntReturnType],
465
                    ]
466
            );
467
        }
468
469
        $operators[] = [INativeType::TYPE_INT, $operator, INativeType::TYPE_INT, 'return' => $otherIntReturnType];
470
        $operators[] = [
471
                INativeType::TYPE_INT,
472
                $operator,
473
                INativeType::TYPE_DOUBLE,
474
                'return' => INativeType::TYPE_DOUBLE
475
        ];
476
        $operators[] = [
477
                INativeType::TYPE_DOUBLE,
478
                $operator,
479
                INativeType::TYPE_DOUBLE,
480
                'return' => INativeType::TYPE_DOUBLE
481
        ];
482
483
        return $operators;
484
    }
485
486
    protected function bitwiseOperators($operator)
487
    {
488
        //TODO: remove duplicate operators with types on opposite sides (binary operators are symmetrical)
489
        $operators = [];
490
        foreach ([
491
                         INativeType::TYPE_INT,
492
                         INativeType::TYPE_DOUBLE,
493
                         INativeType::TYPE_NUMERIC,
494
                         INativeType::TYPE_STRING,
495
                         INativeType::TYPE_RESOURCE,
496
                         INativeType::TYPE_BOOL,
497
                         INativeType::TYPE_NULL
498
                 ] as $type) {
499
            $operators = array_merge(
500
                    $operators,
501
                    [
502
                            [$type, $operator, INativeType::TYPE_INT, 'return' => INativeType::TYPE_INT],
503
                            [$type, $operator, INativeType::TYPE_DOUBLE, 'return' => INativeType::TYPE_INT],
504
                            [$type, $operator, INativeType::TYPE_NULL, 'return' => INativeType::TYPE_INT],
505
                            [$type, $operator, INativeType::TYPE_BOOL, 'return' => INativeType::TYPE_INT],
506
                            [$type, $operator, INativeType::TYPE_STRING, 'return' => INativeType::TYPE_INT],
507
                            [$type, $operator, INativeType::TYPE_RESOURCE, 'return' => INativeType::TYPE_INT],
508
                    ]
509
            );
510
        }
511
512
        return $operators;
513
    }
514
515
    protected function binaryOperations()
516
    {
517
        return array_merge(
518
                [
519
                        $this->booleanOperator(Operators\Binary::EQUALITY),
520
                        $this->booleanOperator(Operators\Binary::INEQUALITY),
521
                        $this->booleanOperator(Operators\Binary::IDENTITY),
522
                        $this->booleanOperator(Operators\Binary::NOT_IDENTICAL),
523
                        $this->booleanOperator(Operators\Binary::GREATER_THAN),
524
                        $this->booleanOperator(Operators\Binary::GREATER_THAN_OR_EQUAL_TO),
525
                        $this->booleanOperator(Operators\Binary::LESS_THAN),
526
                        $this->booleanOperator(Operators\Binary::LESS_THAN_OR_EQUAL_TO),
527
                        $this->booleanOperator(Operators\Binary::IS_INSTANCE_OF),
528
                        $this->booleanOperator(Operators\Binary::EQUALITY),
529
                        $this->booleanOperator(Operators\Binary::LOGICAL_AND),
530
                        $this->booleanOperator(Operators\Binary::LOGICAL_OR),
531
                        [
532
                                INativeType::TYPE_MIXED,
533
                                Operators\Binary::CONCATENATION,
534
                                INativeType::TYPE_MIXED,
535
                                'return' => INativeType::TYPE_STRING
536
                        ],
537
                        [
538
                                INativeType::TYPE_ARRAY,
539
                                Operators\Binary::ADDITION,
540
                                INativeType::TYPE_ARRAY,
541
                                'return' => INativeType::TYPE_ARRAY
542
                        ],
543
                ],
544
                $this->mathOperators(Operators\Binary::ADDITION),
545
                $this->mathOperators(Operators\Binary::SUBTRACTION),
546
                $this->mathOperators(Operators\Binary::MULTIPLICATION),
547
                $this->mathOperators(Operators\Binary::DIVISION, INativeType::TYPE_NUMERIC),
548
                $this->mathOperators(Operators\Binary::MODULUS),
549
                $this->mathOperators(Operators\Binary::POWER),
550
                $this->bitwiseOperators(Operators\Binary::BITWISE_AND),
551
                $this->bitwiseOperators(Operators\Binary::BITWISE_OR),
552
                $this->bitwiseOperators(Operators\Binary::BITWISE_XOR),
553
                $this->bitwiseOperators(Operators\Binary::SHIFT_RIGHT),
554
                $this->bitwiseOperators(Operators\Binary::SHIFT_LEFT)
555
        );
556
    }
557
}
558