Issues (132)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Compiler/Expression.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace PHPSA\Compiler;
7
8
use InvalidArgumentException;
9
use phpDocumentor\Reflection\DocBlockFactory;
10
use PHPSA\CompiledExpression;
11
use PHPSA\Compiler\Event\ExpressionBeforeCompile;
12
use PHPSA\Context;
13
use PhpParser\Node;
14
use PHPSA\Definition\ClassDefinition;
15
use PHPSA\Exception\RuntimeException;
16
use PHPSA\Variable;
17
use PHPSA\Compiler\Expression\AbstractExpressionCompiler;
18
use Webiny\Component\EventManager\EventManager;
19
20
class Expression
0 ignored issues
show
This class has a complexity of 104 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

You can also find more detailed suggestions for refactoring in the “Code” section of your repository.

Loading history...
21
{
22
    /**
23
     * @var Context
24
     */
25
    protected $context;
26
27
    /**
28
     * @var EventManager
29
     */
30
    protected $eventManager;
31
32
    /**
33
     * @param Context $context
34
     */
35
    public function __construct(Context $context, EventManager $eventManager)
36
    {
37
        $this->context = $context;
38
        $this->eventManager = $eventManager;
39
    }
40
41
    /**
42
     * @param $expr
43
     * @return ExpressionCompilerInterface|AbstractExpressionCompiler
44
     */
45
    protected function factory($expr)
46
    {
47
        switch (get_class($expr)) {
48
            /**
49
             * Call(s)
50
             */
51
            case Node\Expr\MethodCall::class:
52
                return new Expression\MethodCall();
53
            case Node\Expr\FuncCall::class:
54
                return new Expression\FunctionCall();
55
            case Node\Expr\StaticCall::class:
56
                return new Expression\StaticCall();
57
            /**
58
             * Operators
59
             */
60
            case Node\Expr\New_::class:
61
                return new Expression\Operators\NewOp();
62
            case Node\Expr\Instanceof_::class:
63
                return new Expression\Operators\InstanceOfOp();
64
            /**
65
             * AssignOp
66
             */
67
            case Node\Expr\AssignOp\Pow::class:
68
                return new Expression\AssignOp\Pow();
69
            case Node\Expr\AssignOp\Plus::class:
70
                return new Expression\AssignOp\Plus();
71
            case Node\Expr\AssignOp\Minus::class:
72
                return new Expression\AssignOp\Minus();
73
            case Node\Expr\AssignOp\Mul::class:
74
                return new Expression\AssignOp\Mul();
75
            case Node\Expr\AssignOp\Div::class:
76
                return new Expression\AssignOp\Div();
77
            case Node\Expr\AssignOp\Mod::class:
78
                return new Expression\AssignOp\Mod();
79
            case Node\Expr\AssignOp\BitwiseOr::class:
80
                return new Expression\AssignOp\BitwiseOr();
81
            case Node\Expr\AssignOp\BitwiseAnd::class:
82
                return new Expression\AssignOp\BitwiseAnd();
83
            case Node\Expr\AssignOp\BitwiseXor::class:
84
                return new Expression\AssignOp\BitwiseXor();
85
            case Node\Expr\AssignOp\Concat::class:
86
                return new Expression\AssignOp\Concat();
87
            case Node\Expr\AssignOp\ShiftLeft::class:
88
                return new Expression\AssignOp\ShiftLeft();
89
            case Node\Expr\AssignOp\ShiftRight::class:
90
                return new Expression\AssignOp\ShiftRight();
91
92
            /**
93
             * BinaryOp
94
             */
95
            case Node\Expr\BinaryOp\Identical::class:
96
                return new Expression\BinaryOp\Identical();
97
            case Node\Expr\BinaryOp\Concat::class:
98
                return new Expression\Operators\Concat();
99
            case Node\Expr\BinaryOp\NotIdentical::class:
100
                return new Expression\BinaryOp\NotIdentical();
101
            case Node\Expr\BinaryOp\Equal::class:
102
                return new Expression\BinaryOp\Equal();
103
            case Node\Expr\BinaryOp\NotEqual::class:
104
                return new Expression\BinaryOp\NotEqual();
105
            case Node\Expr\BinaryOp\Spaceship::class:
106
                return new Expression\BinaryOp\SpaceShip();
107
            case Node\Expr\BinaryOp\Coalesce::class:
108
                return new Expression\BinaryOp\Coalesce();
109
                
110
            /**
111
             * @link http://php.net/manual/en/language.operators.increment.php
112
             */
113
            case Node\Expr\PostInc::class:
114
                return new Expression\Operators\PostInc();
115
            case Node\Expr\PostDec::class:
116
                return new Expression\Operators\PostDec();
117
            case Node\Expr\PreInc::class:
118
                return new Expression\Operators\PreInc();
119
            case Node\Expr\PreDec::class:
120
                return new Expression\Operators\PreDec();
121
            /**
122
             * Arithmetical
123
             */
124
            case Node\Expr\BinaryOp\Div::class:
125
                return new Expression\Operators\Arithmetical\Div();
126
            case Node\Expr\BinaryOp\Plus::class:
127
                return new Expression\Operators\Arithmetical\Plus();
128
            case Node\Expr\BinaryOp\Minus::class:
129
                return new Expression\Operators\Arithmetical\Minus();
130
            case Node\Expr\BinaryOp\Mul::class:
131
                return new Expression\Operators\Arithmetical\Mul();
132
            case Node\Expr\BinaryOp\Mod::class:
133
                return new Expression\Operators\Arithmetical\Mod();
134
            case Node\Expr\BinaryOp\Pow::class:
135
                return new Expression\Operators\Arithmetical\Pow();
136
137
            /**
138
             * Bitwise
139
             * @link http://php.net/manual/ru/language.operators.bitwise.php
140
             */
141
            case Node\Expr\BinaryOp\BitwiseOr::class:
142
                return new Expression\Operators\Bitwise\BitwiseOr();
143
            case Node\Expr\BinaryOp\BitwiseXor::class:
144
                return new Expression\Operators\Bitwise\BitwiseXor();
145
            case Node\Expr\BinaryOp\BitwiseAnd::class:
146
                return new Expression\Operators\Bitwise\BitwiseAnd();
147
            case Node\Expr\BinaryOp\ShiftRight::class:
148
                return new Expression\Operators\Bitwise\ShiftRight();
149
            case Node\Expr\BinaryOp\ShiftLeft::class:
150
                return new Expression\Operators\Bitwise\ShiftLeft();
151
            case Node\Expr\BitwiseNot::class:
152
                return new Expression\Operators\Bitwise\BitwiseNot();
153
            /**
154
             * Logical
155
             */
156
            case Node\Expr\BinaryOp\BooleanOr::class:
157
                return new Expression\Operators\Logical\BooleanOr();
158
            case Node\Expr\BinaryOp\BooleanAnd::class:
159
                return new Expression\Operators\Logical\BooleanAnd();
160
            case Node\Expr\BooleanNot::class:
161
                return new Expression\Operators\Logical\BooleanNot();
162
            case Node\Expr\BinaryOp\LogicalAnd::class:
163
                return new Expression\Operators\Logical\LogicalAnd();
164
            case Node\Expr\BinaryOp\LogicalOr::class:
165
                return new Expression\Operators\Logical\LogicalOr();
166
            case Node\Expr\BinaryOp\LogicalXor::class:
167
                return new Expression\Operators\Logical\LogicalXor();
168
169
            /**
170
             * Comparison
171
             */
172
            case Node\Expr\BinaryOp\Greater::class:
173
                return new Expression\Operators\Comparison\Greater();
174
            case Node\Expr\BinaryOp\GreaterOrEqual::class:
175
                return new Expression\Operators\Comparison\GreaterOrEqual();
176
            case Node\Expr\BinaryOp\Smaller::class:
177
                return new Expression\Operators\Comparison\Smaller();
178
            case Node\Expr\BinaryOp\SmallerOrEqual::class:
179
                return new Expression\Operators\Comparison\SmallerOrEqual();
180
181
            /**
182
             * Casts
183
             */
184
            case Node\Expr\Cast\Array_::class:
185
                return new Expression\Casts\ArrayCast();
186
            case Node\Expr\Cast\Bool_::class:
187
                return new Expression\Casts\BoolCast();
188
            case Node\Expr\Cast\Int_::class:
189
                return new Expression\Casts\IntCast();
190
            case Node\Expr\Cast\Double::class:
191
                return new Expression\Casts\DoubleCast();
192
            case Node\Expr\Cast\Object_::class:
193
                return new Expression\Casts\ObjectCast();
194
            case Node\Expr\Cast\String_::class:
195
                return new Expression\Casts\StringCast();
196
            case Node\Expr\Cast\Unset_::class:
197
                return new Expression\Casts\UnsetCast();
198
199
200
            /**
201
             * Other
202
             */
203
            case Node\Expr\Array_::class:
204
                return new Expression\ArrayOp();
205
            case Node\Expr\Assign::class:
206
                return new Expression\Assign();
207
            case Node\Expr\AssignRef::class:
208
                return new Expression\AssignRef();
209
            case Node\Expr\Closure::class:
210
                return new Expression\Closure();
211
            case Node\Expr\ConstFetch::class:
212
                return new Expression\ConstFetch();
213
            case Node\Expr\ClassConstFetch::class:
214
                return new Expression\ClassConstFetch();
215
            case Node\Expr\PropertyFetch::class:
216
                return new Expression\PropertyFetch();
217
            case Node\Expr\StaticPropertyFetch::class:
218
                return new Expression\StaticPropertyFetch();
219
            case Node\Expr\ArrayDimFetch::class:
220
                return new Expression\ArrayDimFetch();
221
            case Node\Expr\UnaryMinus::class:
222
                return new Expression\Operators\UnaryMinus();
223
            case Node\Expr\UnaryPlus::class:
224
                return new Expression\Operators\UnaryPlus();
225
            case Node\Expr\Exit_::class:
226
                return new Expression\ExitOp();
227
            case Node\Expr\Isset_::class:
228
                return new Expression\IssetOp();
229
            case Node\Expr\Print_::class:
230
                return new Expression\PrintOp();
231
            case Node\Expr\Empty_::class:
232
                return new Expression\EmptyOp();
233
            case Node\Expr\Eval_::class:
234
                return new Expression\EvalOp();
235
            case Node\Expr\ShellExec::class:
236
                return new Expression\ShellExec();
237
            case Node\Expr\ErrorSuppress::class:
238
                return new Expression\ErrorSuppress();
239
            case Node\Expr\Include_::class:
240
                return new Expression\IncludeOp();
241
            case Node\Expr\Clone_::class:
242
                return new Expression\CloneOp();
243
            case Node\Expr\Ternary::class:
244
                return new Expression\Ternary();
245
            case Node\Expr\Yield_::class:
246
                return new Expression\YieldOp();
247
            case Node\Expr\YieldFrom::class:
248
                return new Expression\YieldFrom();
249
            case Node\Expr\Variable::class:
250
                return new Expression\Variable();
251
        }
252
253
        return false;
254
    }
255
256
    /**
257
     * @param object|string $expr
258
     * @throws InvalidArgumentException when $expr is not string/object/null
259
     * @throws RuntimeException when compiler class does not return a CompiledExpression
260
     * @return CompiledExpression
261
     */
262
    public function compile($expr)
263
    {
264
        if (is_string($expr)) {
265
            return new CompiledExpression(CompiledExpression::STRING, $expr);
266
        }
267
268
        if (is_null($expr)) {
269
            return new CompiledExpression(CompiledExpression::NULL);
270
        }
271
272
        if (!is_object($expr)) {
273
            throw new InvalidArgumentException('$expr must be string/object/null');
274
        }
275
276
        if ($expr instanceof Node\Scalar) {
277
            $scalar = new \PHPSA\Compiler\Scalar($this->context, $this->eventManager);
278
            return $scalar->compile($expr);
279
        }
280
281
        $this->eventManager->fire(
282
            ExpressionBeforeCompile::EVENT_NAME,
283
            new ExpressionBeforeCompile(
284
                $expr,
285
                $this->context
286
            )
287
        );
288
289
        $className = get_class($expr);
290
        switch ($className) {
291
            case Node\Arg::class:
292
                /**
293
                 * @todo Better compile
294
                 */
295
                return $this->compile($expr->value);
296
297
            /**
298
             * Names
299
             */
300
            case Node\Name::class:
301
                return $this->getNodeName($expr);
302
            case Node\Name\FullyQualified::class:
303
                return $this->getFullyQualifiedNodeName($expr);
304
        }
305
306
        $expressionCompiler = $this->factory($expr);
307
        if (!$expressionCompiler) {
308
            $this->context->debug("Expression compiler is not implemented for {$className}");
309
            return new CompiledExpression(CompiledExpression::UNIMPLEMENTED);
310
        }
311
312
        $result = $expressionCompiler->pass($expr, $this->context);
313
        if (!$result instanceof CompiledExpression) {
314
            throw new RuntimeException('Please return CompiledExpression from ' . get_class($expressionCompiler));
315
        }
316
317
        return $result;
318
    }
319
320
    /**
321
     * @todo Implement - does not belong in this file
322
     *
323
     * @param Node\Stmt\Property $st
324
    public function passProperty(Node\Stmt\Property $st)
325
    {
326
        $docBlock = $st->getDocComment();
327
        if (!$docBlock) {
328
            return new CompiledExpression();
329
        }
330
331
        $phpdoc = DocBlockFactory::createInstance()->create($docBlock->getText());
332
333
        $varTags = $phpdoc->getTagsByName('var');
334
        if ($varTags) {
335
            $varTag = current($varTags);
336
337
            $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
338
339
            try {
340
                $type = $typeResolver->resolve($varTag->getType());
341
            } catch (\InvalidArgumentException $e) {
342
                return new CompiledExpression();
343
            }
344
345
            if ($type) {
346
                switch (get_class($type)) {
347
                    case \phpDocumentor\Reflection\Types\Object_::class:
348
                        return new CompiledExpression(
349
                            CompiledExpression::OBJECT
350
                        );
351
                    case \phpDocumentor\Reflection\Types\Integer::class:
352
                        return new CompiledExpression(
353
                            CompiledExpression::INTEGER
354
                        );
355
                    case \phpDocumentor\Reflection\Types\String_::class:
356
                        return new CompiledExpression(
357
                            CompiledExpression::STRING
358
                        );
359
                    case \phpDocumentor\Reflection\Types\Float_::class:
360
                        return new CompiledExpression(
361
                            CompiledExpression::DOUBLE
362
                        );
363
                    case \phpDocumentor\Reflection\Types\Null_::class:
364
                        return new CompiledExpression(
365
                            CompiledExpression::NULL
366
                        );
367
                    case \phpDocumentor\Reflection\Types\Boolean::class:
368
                        return new CompiledExpression(
369
                            CompiledExpression::BOOLEAN
370
                        );
371
                }
372
            }
373
        }
374
375
        return new CompiledExpression();
376
    }
377
*/
378
379
380
    /**
381
     * @param Node\Expr\Variable $expr
382
     * @param mixed $value
383
     * @param int $type
384
     * @return CompiledExpression
385
     */
386
    public function declareVariable(Node\Expr\Variable $expr, $value = null, $type = CompiledExpression::UNKNOWN)
387
    {
388
        $variable = $this->context->getSymbol($expr->name);
389
        if ($variable) {
390
            $variable->modify($type, $value);
391
        } else {
392
            $variable = new Variable($expr->name, $value, $type, $this->context->getCurrentBranch());
0 ignored issues
show
It seems like $expr->name can also be of type object<PhpParser\Node\Expr>; however, PHPSA\Variable::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
393
            $this->context->addVariable($variable);
394
        }
395
396
        return new CompiledExpression($variable->getType(), $variable->getValue(), $variable);
397
    }
398
399
    /**
400
     * @param Node\Name\FullyQualified $expr
401
     * @return CompiledExpression
402
     */
403
    public function getFullyQualifiedNodeName(Node\Name\FullyQualified $expr)
404
    {
405
        $compiler = $this->context->getApplication()->compiler;
406
407
        $classDefinition = $compiler->getClass($expr->toString());
408
        if ($classDefinition) {
409
            return new CompiledExpression(
410
                CompiledExpression::OBJECT,
411
                $classDefinition
412
            );
413
        }
414
415
        $this->context->debug('Unimplemented FullyQualified', $expr);
416
        return new CompiledExpression();
417
    }
418
419
    /**
420
     * @param Node\Name $expr
421
     * @return CompiledExpression
422
     */
423
    public function getNodeName(Node\Name $expr)
424
    {
425
        $nodeString = $expr->toString();
426
        if ($nodeString === 'null') {
427
            return new CompiledExpression(CompiledExpression::NULL);
428
        }
429
430
        if (in_array($nodeString, ['parent'], true)) {
431
            /** @var ClassDefinition $scope */
432
            $scope = $this->context->scope;
433
            assert($scope instanceof ClassDefinition);
434
435
            if ($scope->getExtendsClass()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $scope->getExtendsClass() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
436
                $definition = $scope->getExtendsClassDefinition();
437
                if ($definition) {
438
                    return new CompiledExpression(CompiledExpression::OBJECT, $definition);
439
                }
440
            } else {
441
                $this->context->notice(
442
                    'language_error',
443
                    'Cannot access parent:: when current class scope has no parent',
444
                    $expr
445
                );
446
            }
447
        }
448
449
        if (in_array($nodeString, ['self', 'static'], true)) {
450
            return CompiledExpression::fromZvalValue($this->context->scope);
451
        }
452
453
        if (defined($nodeString)) {
454
            return CompiledExpression::fromZvalValue(constant($expr));
455
        }
456
457
        return new CompiledExpression(CompiledExpression::STRING, $expr->toString());
458
    }
459
}
460