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.

ExpressionAnalyser   F
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 358
Duplicated Lines 10.61 %

Coupling/Cohesion

Components 1
Dependencies 39

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 39
dl 38
loc 358
rs 3.7614
c 0
b 0
f 0

34 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getTypeSystem() 0 4 1
A createAnalysisContext() 0 4 1
A analyse() 0 10 1
A visitArray() 0 5 1
A visitArrayItem() 0 5 1
B visitAssignment() 0 26 3
A visitBinaryOperation() 0 13 1
A addTypeOperation() 0 5 1
A visitUnaryOperation() 0 8 1
A visitCast() 0 8 1
A visitConstant() 0 6 1
A visitClassConstant() 0 7 1
A verifyConstantDefined() 0 6 2
A visitEmpty() 0 5 1
A visitIsset() 0 5 1
A visitUnset() 0 5 1
A visitField() 0 10 1
A visitMethodCall() 0 11 1
A visitIndex() 0 10 1
A visitInvocation() 0 10 1
A visitFunctionCall() 0 14 2
A validateStaticClassName() 0 8 2
A visitStaticMethodCall() 13 13 1
A visitStaticField() 13 13 1
A visitNew() 12 12 1
A visitTernary() 0 11 2
A visitVariable() 0 14 2
A visitValue() 0 4 1
B visitClosure() 0 30 5
A visitReturn() 0 4 1
A visitThrow() 0 4 1
A visitParameter() 0 4 1
A visitArgument() 0 4 1

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

1
<?php
2
3
namespace Pinq\Analysis;
4
5
use Pinq\Expressions as O;
6
7
/**
8
 * Implementation of the expression type analyser.
9
 *
10
 * @author Elliot Levin <[email protected]>
11
 */
12
class ExpressionAnalyser extends O\ExpressionVisitor implements IExpressionAnalyser
13
{
14
    /**
15
     * @var ITypeSystem
16
     */
17
    protected $typeSystem;
18
19
    /**
20
     * @var IAnalysisContext
21
     */
22
    protected $analysisContext;
23
24
    /**
25
     * @var \SplObjectStorage|IType[]
26
     */
27
    protected $analysis;
28
29
    /**
30
     * @var \SplObjectStorage
31
     */
32
    protected $metadata;
33
34
    public function __construct(ITypeSystem $typeSystem)
35
    {
36
        $this->typeSystem = $typeSystem;
37
    }
38
39
    public function getTypeSystem()
40
    {
41
        return $this->typeSystem;
42
    }
43
44
    public function createAnalysisContext(O\IEvaluationContext $evaluationContext)
45
    {
46
        return new AnalysisContext($this->typeSystem, $evaluationContext);
47
    }
48
49
    public function analyse(IAnalysisContext $analysisContext, O\Expression $expression)
50
    {
51
        $this->analysisContext = $analysisContext;
52
        $this->analysis        = new \SplObjectStorage();
53
        $this->metadata        = new \SplObjectStorage();
54
55
        $this->walk($expression);
56
57
        return new TypeAnalysis($this->typeSystem, $expression, $this->analysis, $this->metadata);
58
    }
59
60
    public function visitArray(O\ArrayExpression $expression)
61
    {
62
        $this->walkAll($expression->getItems());
63
        $this->analysis[$expression] = $this->typeSystem->getNativeType(INativeType::TYPE_ARRAY);
64
    }
65
66
    public function visitArrayItem(O\ArrayItemExpression $expression)
67
    {
68
        $this->walk($expression->getKey());
69
        $this->walk($expression->getValue());
70
    }
71
72
    public function visitAssignment(O\AssignmentExpression $expression)
73
    {
74
        $assignTo = $expression->getAssignTo();
75
        $assignmentValue = $expression->getAssignmentValue();
76
77
        $this->walk($assignmentValue);
78
79
        $operator = $expression->getOperator();
80
        if ($operator === O\Operators\Assignment::EQUAL) {
81
            $this->analysisContext->setExpressionType($assignTo, $this->analysis[$assignmentValue]);
82
            $this->analysis[$expression] = $this->analysis[$assignmentValue];
83
        } elseif ($operator === O\Operators\Assignment::EQUAL_REFERENCE) {
84
            $this->analysisContext->removeExpressionType($assignTo);
85
            $this->analysisContext->setExpressionType($assignTo, $this->analysis[$assignmentValue]);
86
            $this->analysisContext->createReference($assignTo, $assignmentValue);
87
            $this->analysis[$expression] = $this->analysis[$assignmentValue];
88
        } else {
89
            $this->walk($assignTo);
90
            $binaryOperation             = $this->typeSystem->getBinaryOperation(
91
                    $this->analysis[$assignTo],
92
                    O\Operators\Assignment::toBinaryOperator($operator),
93
                    $this->analysis[$assignmentValue]
94
            );
95
            $this->analysis[$expression] = $binaryOperation->getReturnType();
96
        }
97
    }
98
99
    public function visitBinaryOperation(O\BinaryOperationExpression $expression)
100
    {
101
        $this->walk($expression->getLeftOperand());
102
        $this->walk($expression->getRightOperand());
103
104
        $binaryOperation             = $this->typeSystem->getBinaryOperation(
105
                $this->analysis[$expression->getLeftOperand()],
106
                $expression->getOperator(),
107
                $this->analysis[$expression->getRightOperand()]
108
        );
109
        $this->metadata[$expression] = $binaryOperation;
110
        $this->analysis[$expression] = $binaryOperation->getReturnType();
111
    }
112
113
    protected function addTypeOperation(O\Expression $expression, ITypeOperation $typeOperation)
114
    {
115
        $this->metadata[$expression] = $typeOperation;
116
        $this->analysis[$expression] = $typeOperation->getReturnType();
117
    }
118
119
    public function visitUnaryOperation(O\UnaryOperationExpression $expression)
120
    {
121
        $this->walk($expression->getOperand());
122
        $this->addTypeOperation(
123
                $expression,
124
                $this->analysis[$expression->getOperand()]->getUnaryOperation($expression)
125
        );
126
    }
127
128
    public function visitCast(O\CastExpression $expression)
129
    {
130
        $this->walk($expression->getCastValue());
131
        $this->addTypeOperation(
132
                $expression,
133
                $this->analysis[$expression->getCastValue()]->getCast($expression)
134
        );
135
    }
136
137
    public function visitConstant(O\ConstantExpression $expression)
138
    {
139
        $this->verifyConstantDefined($expression->getName());
140
141
        $this->analysis[$expression] = $this->typeSystem->getTypeFromValue($expression->evaluate($this->analysisContext->getEvaluationContext()));
142
    }
143
144
    public function visitClassConstant(O\ClassConstantExpression $expression)
145
    {
146
        $this->validateStaticClassName($expression->getClass(), 'class constant');
147
        $this->verifyConstantDefined($expression->getClass()->getValue() . '::' . $expression->getName());
148
149
        $this->analysis[$expression] = $this->typeSystem->getTypeFromValue($expression->evaluate($this->analysisContext->getEvaluationContext()));
150
    }
151
152
    private function verifyConstantDefined($constantName)
153
    {
154
        if (!defined($constantName)) {
155
            throw new TypeException('Cannot get type from constant %s: constant is not defined', $constantName);
156
        }
157
    }
158
159
    public function visitEmpty(O\EmptyExpression $expression)
160
    {
161
        $this->walk($expression->getValue());
162
        $this->analysis[$expression] = $this->typeSystem->getNativeType(INativeType::TYPE_BOOL);
163
    }
164
165
    public function visitIsset(O\IssetExpression $expression)
166
    {
167
        $this->walkAll($expression->getValues());
168
        $this->analysis[$expression] = $this->typeSystem->getNativeType(INativeType::TYPE_BOOL);
169
    }
170
171
    public function visitUnset(O\UnsetExpression $expression)
172
    {
173
        $this->walkAll($expression->getValues());
174
        $this->analysis[$expression] = $this->typeSystem->getType(INativeType::TYPE_NULL);
175
    }
176
177
    public function visitField(O\FieldExpression $expression)
178
    {
179
        $this->walk($expression->getValue());
180
        $this->walk($expression->getName());
181
182
        $this->addTypeOperation(
183
                $expression,
184
                $this->analysis[$expression->getValue()]->getField($expression)
185
        );
186
    }
187
188
    public function visitMethodCall(O\MethodCallExpression $expression)
189
    {
190
        $this->walk($expression->getValue());
191
        $this->walk($expression->getName());
192
        $this->walkAll($expression->getArguments());
193
194
        $this->addTypeOperation(
195
                $expression,
196
                $this->analysis[$expression->getValue()]->getMethod($expression)
197
        );
198
    }
199
200
    public function visitIndex(O\IndexExpression $expression)
201
    {
202
        $this->walk($expression->getValue());
203
        $this->walk($expression->getIndex());
204
205
        $this->addTypeOperation(
206
                $expression,
207
                $this->analysis[$expression->getValue()]->getIndex($expression)
208
        );
209
    }
210
211
    public function visitInvocation(O\InvocationExpression $expression)
212
    {
213
        $this->walk($expression->getValue());
214
        $this->walkAll($expression->getArguments());
215
216
        $this->addTypeOperation(
217
                $expression,
218
                $this->analysis[$expression->getValue()]->getInvocation($expression)
219
        );
220
    }
221
222
    public function visitFunctionCall(O\FunctionCallExpression $expression)
223
    {
224
        $nameExpression = $expression->getName();
225
        $this->walk($nameExpression);
226
        $this->walkAll($expression->getArguments());
227
228
        if ($nameExpression instanceof O\ValueExpression) {
229
            $function                    = $this->typeSystem->getFunction($nameExpression->getValue());
230
            $this->metadata[$expression] = $function;
231
            $this->analysis[$expression] = $function->getReturnType();
232
        } else {
233
            throw new TypeException('Invalid function expression: dynamic function calls are not allowed');
234
        }
235
    }
236
237
    protected function validateStaticClassName(O\Expression $expression, $type)
238
    {
239
        if ($expression instanceof O\ValueExpression) {
240
            return $expression->getValue();
241
        } else {
242
            throw new TypeException('Invalid %s expression: dynamic class types are not supported', $type);
243
        }
244
    }
245
246 View Code Duplication
    public function visitStaticMethodCall(O\StaticMethodCallExpression $expression)
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...
247
    {
248
        $classExpression = $expression->getClass();
249
        $this->walk($classExpression);
250
        $this->walk($expression->getName());
251
        $this->walkAll($expression->getArguments());
252
253
        $class = $this->validateStaticClassName($classExpression, 'static method call');
254
        $this->addTypeOperation(
255
                $expression,
256
                $this->typeSystem->getObjectType($class)->getStaticMethod($expression)
257
        );
258
    }
259
260 View Code Duplication
    public function visitStaticField(O\StaticFieldExpression $expression)
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...
261
    {
262
        $classExpression = $expression->getClass();
263
        $this->walk($classExpression);
264
        $this->walk($expression->getName());
265
266
        $class = $this->validateStaticClassName($classExpression, 'static field');
267
268
        $this->addTypeOperation(
269
                $expression,
270
                $this->typeSystem->getObjectType($class)->getStaticField($expression)
271
        );
272
    }
273
274 View Code Duplication
    public function visitNew(O\NewExpression $expression)
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...
275
    {
276
        $classExpression = $expression->getClass();
277
        $this->walk($classExpression);
278
        $this->walkAll($expression->getArguments());
279
280
        $class = $this->validateStaticClassName($classExpression, 'new');
281
        $this->addTypeOperation(
282
                $expression,
283
                $this->typeSystem->getObjectType($class)->getConstructor($expression)
284
        );
285
    }
286
287
    public function visitTernary(O\TernaryExpression $expression)
288
    {
289
        $this->walk($expression->getCondition());
290
        $this->walk($expression->getIfTrue());
291
        $this->walk($expression->getIfFalse());
292
293
        $this->analysis[$expression] = $this->typeSystem->getCommonAncestorType(
294
                $this->analysis[$expression->hasIfTrue() ? $expression->getIfTrue() : $expression->getCondition()],
295
                $this->analysis[$expression->getIfFalse()]
296
        );
297
    }
298
299
    public function visitVariable(O\VariableExpression $expression)
300
    {
301
        $nameExpression = $expression->getName();
302
        $this->walk($nameExpression);
303
304
        $type = $this->analysisContext->getExpressionType($expression);
305
        if ($type === null) {
306
            throw new TypeException(
307
                    'Invalid variable expression: \'%s\' type is unknown',
308
                    $nameExpression->compileDebug());
309
        }
310
311
        $this->analysis[$expression] = $type;
312
    }
313
314
    public function visitValue(O\ValueExpression $expression)
315
    {
316
        $this->analysis[$expression] = $this->typeSystem->getTypeFromValue($expression->getValue());
317
    }
318
319
    public function visitClosure(O\ClosureExpression $expression)
320
    {
321
        $originalContext = $this->analysisContext;
322
        $this->analysisContext   = $originalContext->inNewScope();
323
324
        foreach ($expression->getParameters() as $parameter) {
325
            $this->walk($parameter);
326
            $typeHintType = $this->typeSystem->getTypeFromTypeHint($parameter->getTypeHint());
327
            if (!$parameter->hasDefaultValue()
328
                    || $this->analysis[$parameter->getDefaultValue()]->isEqualTo($typeHintType)
329
            ) {
330
                $this->analysisContext->setExpressionType($parameter->asVariable(), $typeHintType);
331
            } else {
332
                $this->analysisContext->setExpressionType(
333
                        $parameter->asVariable(),
334
                        $this->typeSystem->getNativeType(INativeType::TYPE_MIXED)
335
                );
336
            }
337
        }
338
339
        foreach ($expression->getUsedVariables() as $usedVariable) {
340
            $variable = $usedVariable->asVariable();
341
            //TODO: handle references with used variables. Probably impossible though.
342
            $this->analysisContext->setExpressionType($variable, $originalContext->getExpressionType($variable));
343
        }
344
345
        $this->walkAll($expression->getBodyExpressions());
346
        $this->analysis[$expression] = $this->typeSystem->getObjectType('Closure');
347
        $this->analysisContext       = $originalContext;
348
    }
349
350
    public function visitReturn(O\ReturnExpression $expression)
351
    {
352
        $this->walk($expression->getValue());
353
    }
354
355
    public function visitThrow(O\ThrowExpression $expression)
356
    {
357
        $this->walk($expression->getException());
358
    }
359
360
    public function visitParameter(O\ParameterExpression $expression)
361
    {
362
        $this->walk($expression->getDefaultValue());
363
    }
364
365
    public function visitArgument(O\ArgumentExpression $expression)
366
    {
367
        $this->walk($expression->getValue());
368
    }
369
}
370