Security update for Core, with self-updated composer
[yaffs-website] / vendor / psy / psysh / src / Psy / CodeCleaner / FunctionReturnInWriteContextPass.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2017 Justin Hileman
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Psy\CodeCleaner;
13
14 use PhpParser\Node;
15 use PhpParser\Node\Expr\Array_;
16 use PhpParser\Node\Expr\Assign;
17 use PhpParser\Node\Expr\Empty_;
18 use PhpParser\Node\Expr\FuncCall;
19 use PhpParser\Node\Expr\Isset_;
20 use PhpParser\Node\Expr\MethodCall;
21 use PhpParser\Node\Expr\StaticCall;
22 use PhpParser\Node\Stmt\Unset_;
23 use Psy\Exception\FatalErrorException;
24
25 /**
26  * Validate that the functions are used correctly.
27  *
28  * @author Martin HasoĊˆ <martin.hason@gmail.com>
29  */
30 class FunctionReturnInWriteContextPass extends CodeCleanerPass
31 {
32     const PHP55_MESSAGE = 'Cannot use isset() on the result of a function call (you can use "null !== func()" instead)';
33     const EXCEPTION_MESSAGE = "Can't use function return value in write context";
34
35     private $isPhp55;
36
37     public function __construct()
38     {
39         $this->isPhp55 = version_compare(PHP_VERSION, '5.5', '>=');
40     }
41
42     /**
43      * Validate that the functions are used correctly.
44      *
45      * @throws FatalErrorException if a function is passed as an argument reference
46      * @throws FatalErrorException if a function is used as an argument in the isset
47      * @throws FatalErrorException if a function is used as an argument in the empty, only for PHP < 5.5
48      * @throws FatalErrorException if a value is assigned to a function
49      *
50      * @param Node $node
51      */
52     public function enterNode(Node $node)
53     {
54         if ($node instanceof Array_ || $this->isCallNode($node)) {
55             $items = $node instanceof Array_ ? $node->items : $node->args;
56             foreach ($items as $item) {
57                 if ($item && $item->byRef && $this->isCallNode($item->value)) {
58                     throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
59                 }
60             }
61         } elseif ($node instanceof Isset_ || $node instanceof Unset_) {
62             foreach ($node->vars as $var) {
63                 if (!$this->isCallNode($var)) {
64                     continue;
65                 }
66
67                 $msg = ($node instanceof Isset_ && $this->isPhp55) ? self::PHP55_MESSAGE : self::EXCEPTION_MESSAGE;
68                 throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
69             }
70         } elseif ($node instanceof Empty_ && !$this->isPhp55 && $this->isCallNode($node->expr)) {
71             throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
72         } elseif ($node instanceof Assign && $this->isCallNode($node->var)) {
73             throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, E_ERROR, null, $node->getLine());
74         }
75     }
76
77     private function isCallNode(Node $node)
78     {
79         return $node instanceof FuncCall || $node instanceof MethodCall || $node instanceof StaticCall;
80     }
81 }