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