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::visitClassConstant()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
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