1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Accessory;
4:
5: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
6: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
7: use PHPStan\Reflection\ClassMemberAccessAnswerer;
8: use PHPStan\Reflection\TrivialParametersAcceptor;
9: use PHPStan\TrinaryLogic;
10: use PHPStan\Type\AcceptsResult;
11: use PHPStan\Type\CompoundType;
12: use PHPStan\Type\ErrorType;
13: use PHPStan\Type\IntersectionType;
14: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
15: use PHPStan\Type\Traits\NonGenericTypeTrait;
16: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
17: use PHPStan\Type\Traits\ObjectTypeTrait;
18: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
19: use PHPStan\Type\Type;
20: use PHPStan\Type\UnionType;
21: use PHPStan\Type\VerbosityLevel;
22: use function sprintf;
23:
24: class HasPropertyType implements AccessoryType, CompoundType
25: {
26:
27: use ObjectTypeTrait;
28: use NonGenericTypeTrait;
29: use UndecidedComparisonCompoundTypeTrait;
30: use NonRemoveableTypeTrait;
31: use NonGeneralizableTypeTrait;
32:
33: /** @api */
34: public function __construct(private string $propertyName)
35: {
36: }
37:
38: /**
39: * @return string[]
40: */
41: public function getReferencedClasses(): array
42: {
43: return [];
44: }
45:
46: public function getObjectClassNames(): array
47: {
48: return [];
49: }
50:
51: public function getObjectClassReflections(): array
52: {
53: return [];
54: }
55:
56: public function getConstantStrings(): array
57: {
58: return [];
59: }
60:
61: public function getPropertyName(): string
62: {
63: return $this->propertyName;
64: }
65:
66: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
67: {
68: return $this->acceptsWithReason($type, $strictTypes)->result;
69: }
70:
71: public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult
72: {
73: if ($type instanceof CompoundType) {
74: return $type->isAcceptedWithReasonBy($this, $strictTypes);
75: }
76:
77: return AcceptsResult::createFromBoolean($this->equals($type));
78: }
79:
80: public function isSuperTypeOf(Type $type): TrinaryLogic
81: {
82: return $type->hasProperty($this->propertyName);
83: }
84:
85: public function isSubTypeOf(Type $otherType): TrinaryLogic
86: {
87: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
88: return $otherType->isSuperTypeOf($this);
89: }
90:
91: if ($otherType instanceof self) {
92: $limit = TrinaryLogic::createYes();
93: } else {
94: $limit = TrinaryLogic::createMaybe();
95: }
96:
97: return $limit->and($otherType->hasProperty($this->propertyName));
98: }
99:
100: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
101: {
102: return $this->isAcceptedWithReasonBy($acceptingType, $strictTypes)->result;
103: }
104:
105: public function isAcceptedWithReasonBy(Type $acceptingType, bool $strictTypes): AcceptsResult
106: {
107: return new AcceptsResult($this->isSubTypeOf($acceptingType), []);
108: }
109:
110: public function equals(Type $type): bool
111: {
112: return $type instanceof self
113: && $this->propertyName === $type->propertyName;
114: }
115:
116: public function describe(VerbosityLevel $level): string
117: {
118: return sprintf('hasProperty(%s)', $this->propertyName);
119: }
120:
121: public function hasProperty(string $propertyName): TrinaryLogic
122: {
123: if ($this->propertyName === $propertyName) {
124: return TrinaryLogic::createYes();
125: }
126:
127: return TrinaryLogic::createMaybe();
128: }
129:
130: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
131: {
132: return [new TrivialParametersAcceptor()];
133: }
134:
135: public function getEnumCases(): array
136: {
137: return [];
138: }
139:
140: public function traverse(callable $cb): Type
141: {
142: return $this;
143: }
144:
145: public function traverseSimultaneously(Type $right, callable $cb): Type
146: {
147: return $this;
148: }
149:
150: public function exponentiate(Type $exponent): Type
151: {
152: return new ErrorType();
153: }
154:
155: public function getFiniteTypes(): array
156: {
157: return [];
158: }
159:
160: public static function __set_state(array $properties): Type
161: {
162: return new self($properties['propertyName']);
163: }
164:
165: public function toPhpDocNode(): TypeNode
166: {
167: return new IdentifierTypeNode(''); // no PHPDoc representation
168: }
169:
170: }
171: