1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Analyser;
4:
5: use PhpParser\Node\Scalar\LNumber;
6: use PhpParser\Node\Stmt;
7:
8: /** @api */
9: class StatementResult
10: {
11:
12: /**
13: * @param StatementExitPoint[] $exitPoints
14: * @param ThrowPoint[] $throwPoints
15: */
16: public function __construct(
17: private MutatingScope $scope,
18: private bool $hasYield,
19: private bool $isAlwaysTerminating,
20: private array $exitPoints,
21: private array $throwPoints,
22: )
23: {
24: }
25:
26: public function getScope(): MutatingScope
27: {
28: return $this->scope;
29: }
30:
31: public function hasYield(): bool
32: {
33: return $this->hasYield;
34: }
35:
36: public function isAlwaysTerminating(): bool
37: {
38: return $this->isAlwaysTerminating;
39: }
40:
41: public function filterOutLoopExitPoints(): self
42: {
43: if (!$this->isAlwaysTerminating) {
44: return $this;
45: }
46:
47: foreach ($this->exitPoints as $exitPoint) {
48: $statement = $exitPoint->getStatement();
49: if (!$statement instanceof Stmt\Break_ && !$statement instanceof Stmt\Continue_) {
50: continue;
51: }
52:
53: $num = $statement->num;
54: if (!$num instanceof LNumber) {
55: return new self($this->scope, $this->hasYield, false, $this->exitPoints, $this->throwPoints);
56: }
57:
58: if ($num->value !== 1) {
59: continue;
60: }
61:
62: return new self($this->scope, $this->hasYield, false, $this->exitPoints, $this->throwPoints);
63: }
64:
65: return $this;
66: }
67:
68: /**
69: * @return StatementExitPoint[]
70: */
71: public function getExitPoints(): array
72: {
73: return $this->exitPoints;
74: }
75:
76: /**
77: * @param class-string<Stmt\Continue_>|class-string<Stmt\Break_> $stmtClass
78: * @return StatementExitPoint[]
79: */
80: public function getExitPointsByType(string $stmtClass): array
81: {
82: $exitPoints = [];
83: foreach ($this->exitPoints as $exitPoint) {
84: $statement = $exitPoint->getStatement();
85: if (!$statement instanceof $stmtClass) {
86: continue;
87: }
88:
89: $value = $statement->num;
90: if ($value === null) {
91: $exitPoints[] = $exitPoint;
92: continue;
93: }
94:
95: if (!$value instanceof LNumber) {
96: $exitPoints[] = $exitPoint;
97: continue;
98: }
99:
100: $value = $value->value;
101: if ($value !== 1) {
102: continue;
103: }
104:
105: $exitPoints[] = $exitPoint;
106: }
107:
108: return $exitPoints;
109: }
110:
111: /**
112: * @return StatementExitPoint[]
113: */
114: public function getExitPointsForOuterLoop(): array
115: {
116: $exitPoints = [];
117: foreach ($this->exitPoints as $exitPoint) {
118: $statement = $exitPoint->getStatement();
119: if (!$statement instanceof Stmt\Continue_ && !$statement instanceof Stmt\Break_) {
120: $exitPoints[] = $exitPoint;
121: continue;
122: }
123: if ($statement->num === null) {
124: continue;
125: }
126: if (!$statement->num instanceof LNumber) {
127: continue;
128: }
129: $value = $statement->num->value;
130: if ($value === 1) {
131: continue;
132: }
133:
134: $newNode = null;
135: if ($value > 2) {
136: $newNode = new LNumber($value - 1);
137: }
138: if ($statement instanceof Stmt\Continue_) {
139: $newStatement = new Stmt\Continue_($newNode);
140: } else {
141: $newStatement = new Stmt\Break_($newNode);
142: }
143:
144: $exitPoints[] = new StatementExitPoint($newStatement, $exitPoint->getScope());
145: }
146:
147: return $exitPoints;
148: }
149:
150: /**
151: * @return ThrowPoint[]
152: */
153: public function getThrowPoints(): array
154: {
155: return $this->throwPoints;
156: }
157:
158: }
159: