Yaffs site version 1.1
[yaffs-website] / vendor / psy / psysh / src / Psy / CodeCleaner / LoopContextPass.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\Scalar\DNumber;
16 use PhpParser\Node\Scalar\LNumber;
17 use PhpParser\Node\Stmt\Break_;
18 use PhpParser\Node\Stmt\Continue_;
19 use PhpParser\Node\Stmt\Do_;
20 use PhpParser\Node\Stmt\For_;
21 use PhpParser\Node\Stmt\Foreach_;
22 use PhpParser\Node\Stmt\Switch_;
23 use PhpParser\Node\Stmt\While_;
24 use Psy\Exception\FatalErrorException;
25
26 /**
27  * The loop context pass handles invalid `break` and `continue` statements.
28  */
29 class LoopContextPass extends CodeCleanerPass
30 {
31     private $isPHP54;
32     private $loopDepth;
33
34     public function __construct()
35     {
36         $this->isPHP54 = version_compare(PHP_VERSION, '5.4.0', '>=');
37     }
38
39     /**
40      * {@inheritdoc}
41      */
42     public function beforeTraverse(array $nodes)
43     {
44         $this->loopDepth = 0;
45     }
46
47     /**
48      * @throws FatalErrorException if the node is a break or continue in a non-loop or switch context
49      * @throws FatalErrorException if the node is trying to break out of more nested structures than exist
50      * @throws FatalErrorException if the node is a break or continue and has a non-numeric argument
51      * @throws FatalErrorException if the node is a break or continue and has an argument less than 1
52      *
53      * @param Node $node
54      */
55     public function enterNode(Node $node)
56     {
57         switch (true) {
58             case $node instanceof Do_:
59             case $node instanceof For_:
60             case $node instanceof Foreach_:
61             case $node instanceof Switch_:
62             case $node instanceof While_:
63                 $this->loopDepth++;
64                 break;
65
66             case $node instanceof Break_:
67             case $node instanceof Continue_:
68                 $operator = $node instanceof Break_ ? 'break' : 'continue';
69
70                 if ($this->loopDepth === 0) {
71                     $msg = sprintf("'%s' not in the 'loop' or 'switch' context", $operator);
72                     throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
73                 }
74
75                 if ($node->num instanceof LNumber || $node->num instanceof DNumber) {
76                     $num = $node->num->value;
77                     if ($this->isPHP54 && ($node->num instanceof DNumber || $num < 1)) {
78                         $msg = sprintf("'%s' operator accepts only positive numbers", $operator);
79                         throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
80                     }
81
82                     if ($num > $this->loopDepth) {
83                         $msg = sprintf("Cannot '%s' %d levels", $operator, $num);
84                         throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
85                     }
86                 } elseif ($node->num && $this->isPHP54) {
87                     $msg = sprintf("'%s' operator with non-constant operand is no longer supported", $operator);
88                     throw new FatalErrorException($msg, 0, E_ERROR, null, $node->getLine());
89                 }
90                 break;
91         }
92     }
93
94     /**
95      * @param Node $node
96      */
97     public function leaveNode(Node $node)
98     {
99         switch (true) {
100             case $node instanceof Do_:
101             case $node instanceof For_:
102             case $node instanceof Foreach_:
103             case $node instanceof Switch_:
104             case $node instanceof While_:
105                 $this->loopDepth--;
106                 break;
107         }
108     }
109 }