1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
6: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\Type\Generic\TemplateTypeVariance;
9: use PHPStan\Type\Traits\LateResolvableTypeTrait;
10: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
11: use function count;
12: use function sprintf;
13:
14: /** @api */
15: final class ValueOfType implements CompoundType, LateResolvableType
16: {
17:
18: use LateResolvableTypeTrait;
19: use NonGeneralizableTypeTrait;
20:
21: public function __construct(private Type $type)
22: {
23: }
24:
25: public function getReferencedClasses(): array
26: {
27: return $this->type->getReferencedClasses();
28: }
29:
30: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
31: {
32: return $this->type->getReferencedTemplateTypes($positionVariance);
33: }
34:
35: public function equals(Type $type): bool
36: {
37: return $type instanceof self
38: && $this->type->equals($type->type);
39: }
40:
41: public function describe(VerbosityLevel $level): string
42: {
43: return sprintf('value-of<%s>', $this->type->describe($level));
44: }
45:
46: public function isResolvable(): bool
47: {
48: return !TypeUtils::containsTemplateType($this->type);
49: }
50:
51: protected function getResult(): Type
52: {
53: if ($this->type->isEnum()->yes()) {
54: $valueTypes = [];
55: foreach ($this->type->getEnumCases() as $enumCase) {
56: $valueType = $enumCase->getBackingValueType();
57: if ($valueType === null) {
58: continue;
59: }
60:
61: $valueTypes[] = $valueType;
62: }
63:
64: if (count($valueTypes) === 0) {
65: return new NeverType();
66: }
67: if (count($valueTypes) === 1) {
68: return $valueTypes[0];
69: }
70:
71: return new UnionType($valueTypes);
72: }
73:
74: return $this->type->getIterableValueType();
75: }
76:
77: /**
78: * @param callable(Type): Type $cb
79: */
80: public function traverse(callable $cb): Type
81: {
82: $type = $cb($this->type);
83:
84: if ($this->type === $type) {
85: return $this;
86: }
87:
88: return new self($type);
89: }
90:
91: public function traverseSimultaneously(Type $right, callable $cb): Type
92: {
93: if (!$right instanceof self) {
94: return $this;
95: }
96:
97: $type = $cb($this->type, $right->type);
98:
99: if ($this->type === $type) {
100: return $this;
101: }
102:
103: return new self($type);
104: }
105:
106: public function toPhpDocNode(): TypeNode
107: {
108: return new GenericTypeNode(new IdentifierTypeNode('value-of'), [$this->type->toPhpDocNode()]);
109: }
110:
111: }
112: