1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Reflection;
4:
5: use PHPStan\PhpDoc\ResolvedPhpDocBlock;
6: use PHPStan\PhpDoc\Tag\AssertTag;
7: use PHPStan\Type\Type;
8: use function array_filter;
9: use function array_map;
10: use function array_merge;
11: use function count;
12:
13: /**
14: * @api
15: * @final
16: */
17: class Assertions
18: {
19:
20: private static ?self $empty = null;
21:
22: /**
23: * @param AssertTag[] $asserts
24: */
25: private function __construct(private array $asserts)
26: {
27: }
28:
29: /**
30: * @return AssertTag[]
31: */
32: public function getAll(): array
33: {
34: return $this->asserts;
35: }
36:
37: /**
38: * @return AssertTag[]
39: */
40: public function getAsserts(): array
41: {
42: return array_filter($this->asserts, static fn (AssertTag $assert) => $assert->getIf() === AssertTag::NULL);
43: }
44:
45: /**
46: * @return AssertTag[]
47: */
48: public function getAssertsIfTrue(): array
49: {
50: return array_merge(
51: array_filter($this->asserts, static fn (AssertTag $assert) => $assert->getIf() === AssertTag::IF_TRUE),
52: array_map(
53: static fn (AssertTag $assert) => $assert->negate(),
54: array_filter($this->asserts, static fn (AssertTag $assert) => $assert->getIf() === AssertTag::IF_FALSE && !$assert->isEquality()),
55: ),
56: );
57: }
58:
59: /**
60: * @return AssertTag[]
61: */
62: public function getAssertsIfFalse(): array
63: {
64: return array_merge(
65: array_filter($this->asserts, static fn (AssertTag $assert) => $assert->getIf() === AssertTag::IF_FALSE),
66: array_map(
67: static fn (AssertTag $assert) => $assert->negate(),
68: array_filter($this->asserts, static fn (AssertTag $assert) => $assert->getIf() === AssertTag::IF_TRUE && !$assert->isEquality()),
69: ),
70: );
71: }
72:
73: /**
74: * @param callable(Type): Type $callable
75: */
76: public function mapTypes(callable $callable): self
77: {
78: $assertTagsCallback = static fn (AssertTag $tag): AssertTag => $tag->withType($callable($tag->getType()));
79:
80: return new self(array_map($assertTagsCallback, $this->asserts));
81: }
82:
83: public function intersectWith(Assertions $other): self
84: {
85: return new self(array_merge($this->getAll(), $other->getAll()));
86: }
87:
88: public static function createEmpty(): self
89: {
90: $empty = self::$empty;
91:
92: if ($empty !== null) {
93: return $empty;
94: }
95:
96: $empty = new self([]);
97: self::$empty = $empty;
98:
99: return $empty;
100: }
101:
102: public static function createFromResolvedPhpDocBlock(ResolvedPhpDocBlock $phpDocBlock): self
103: {
104: $tags = $phpDocBlock->getAssertTags();
105: if (count($tags) === 0) {
106: return self::createEmpty();
107: }
108:
109: return new self($tags);
110: }
111:
112: }
113: