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