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\Enum\EnumCaseObjectType;
13: use PHPStan\Type\ErrorType;
14: use PHPStan\Type\Generic\GenericClassStringType;
15: use PHPStan\Type\IntersectionType;
16: use PHPStan\Type\IsSuperTypeOfResult;
17: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
18: use PHPStan\Type\Traits\NonGenericTypeTrait;
19: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
20: use PHPStan\Type\Traits\ObjectTypeTrait;
21: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
22: use PHPStan\Type\Type;
23: use PHPStan\Type\UnionType;
24: use PHPStan\Type\VerbosityLevel;
25: use function sprintf;
26:
27: class HasPropertyType implements AccessoryType, CompoundType
28: {
29:
30: use ObjectTypeTrait;
31: use NonGenericTypeTrait;
32: use UndecidedComparisonCompoundTypeTrait;
33: use NonRemoveableTypeTrait;
34: use NonGeneralizableTypeTrait;
35:
36: /** @api */
37: public function __construct(private string $propertyName)
38: {
39: }
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 getClassStringType(): Type
57: {
58: return new GenericClassStringType($this);
59: }
60:
61: public function getConstantStrings(): array
62: {
63: return [];
64: }
65:
66: public function getPropertyName(): string
67: {
68: return $this->propertyName;
69: }
70:
71: public function accepts(Type $type, bool $strictTypes): AcceptsResult
72: {
73: if ($type instanceof CompoundType) {
74: return $type->isAcceptedBy($this, $strictTypes);
75: }
76:
77: return AcceptsResult::createFromBoolean($this->equals($type));
78: }
79:
80: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
81: {
82: return new IsSuperTypeOfResult(
83: $type->hasInstanceProperty($this->propertyName)->or($type->hasStaticProperty($this->propertyName)),
84: [],
85: );
86: }
87:
88: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
89: {
90: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
91: return $otherType->isSuperTypeOf($this);
92: }
93:
94: if ($otherType instanceof self) {
95: $limit = TrinaryLogic::createYes();
96: } else {
97: $limit = TrinaryLogic::createMaybe();
98: }
99:
100: return new IsSuperTypeOfResult(
101: $limit->and($otherType->hasInstanceProperty($this->propertyName)->or($otherType->hasStaticProperty($this->propertyName))),
102: [],
103: );
104: }
105:
106: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
107: {
108: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
109: }
110:
111: public function equals(Type $type): bool
112: {
113: return $type instanceof self
114: && $this->propertyName === $type->propertyName;
115: }
116:
117: public function describe(VerbosityLevel $level): string
118: {
119: return sprintf('hasProperty(%s)', $this->propertyName);
120: }
121:
122: public function hasProperty(string $propertyName): TrinaryLogic
123: {
124: if ($this->propertyName === $propertyName) {
125: return TrinaryLogic::createYes();
126: }
127:
128: return TrinaryLogic::createMaybe();
129: }
130:
131: public function hasInstanceProperty(string $propertyName): TrinaryLogic
132: {
133: if ($this->propertyName === $propertyName) {
134: return TrinaryLogic::createYes();
135: }
136:
137: return TrinaryLogic::createMaybe();
138: }
139:
140: public function hasStaticProperty(string $propertyName): TrinaryLogic
141: {
142: if ($this->propertyName === $propertyName) {
143: return TrinaryLogic::createYes();
144: }
145:
146: return TrinaryLogic::createMaybe();
147: }
148:
149: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
150: {
151: return [new TrivialParametersAcceptor()];
152: }
153:
154: public function getEnumCases(): array
155: {
156: return [];
157: }
158:
159: public function getEnumCaseObject(): ?EnumCaseObjectType
160: {
161: return null;
162: }
163:
164: public function traverse(callable $cb): Type
165: {
166: return $this;
167: }
168:
169: public function traverseSimultaneously(Type $right, callable $cb): Type
170: {
171: return $this;
172: }
173:
174: public function exponentiate(Type $exponent): Type
175: {
176: return new ErrorType();
177: }
178:
179: public function getFiniteTypes(): array
180: {
181: return [];
182: }
183:
184: public function toPhpDocNode(): TypeNode
185: {
186: return new IdentifierTypeNode(''); // no PHPDoc representation
187: }
188:
189: public function hasTemplateOrLateResolvableType(): bool
190: {
191: return false;
192: }
193:
194: }
195: