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