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