1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Reflection\Php; |
4: | |
5: | use PhpParser\Node; |
6: | use PhpParser\Node\Stmt\ClassMethod; |
7: | use PHPStan\Reflection\Assertions; |
8: | use PHPStan\Reflection\ClassMemberReflection; |
9: | use PHPStan\Reflection\ClassReflection; |
10: | use PHPStan\Reflection\ExtendedMethodReflection; |
11: | use PHPStan\Reflection\MissingMethodFromReflectionException; |
12: | use PHPStan\TrinaryLogic; |
13: | use PHPStan\Type\ArrayType; |
14: | use PHPStan\Type\BooleanType; |
15: | use PHPStan\Type\Generic\TemplateTypeMap; |
16: | use PHPStan\Type\IntegerType; |
17: | use PHPStan\Type\MixedType; |
18: | use PHPStan\Type\ObjectWithoutClassType; |
19: | use PHPStan\Type\StringType; |
20: | use PHPStan\Type\Type; |
21: | use PHPStan\Type\TypeCombinator; |
22: | use PHPStan\Type\VoidType; |
23: | use function in_array; |
24: | use function strtolower; |
25: | |
26: | |
27: | |
28: | |
29: | final class PhpMethodFromParserNodeReflection extends PhpFunctionFromParserNodeReflection implements ExtendedMethodReflection |
30: | { |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | public function __construct( |
40: | private ClassReflection $declaringClass, |
41: | private ClassMethod $classMethod, |
42: | string $fileName, |
43: | TemplateTypeMap $templateTypeMap, |
44: | array $realParameterTypes, |
45: | array $phpDocParameterTypes, |
46: | array $realParameterDefaultValues, |
47: | Type $realReturnType, |
48: | ?Type $phpDocReturnType, |
49: | ?Type $throwType, |
50: | ?string $deprecatedDescription, |
51: | bool $isDeprecated, |
52: | bool $isInternal, |
53: | private bool $isFinal, |
54: | ?bool $isPure, |
55: | bool $acceptsNamedArguments, |
56: | Assertions $assertions, |
57: | private ?Type $selfOutType, |
58: | ?string $phpDocComment, |
59: | array $parameterOutTypes, |
60: | array $immediatelyInvokedCallableParameters, |
61: | array $phpDocClosureThisTypeParameters, |
62: | ) |
63: | { |
64: | $name = strtolower($classMethod->name->name); |
65: | if (in_array($name, ['__construct', '__destruct', '__unset', '__wakeup', '__clone'], true)) { |
66: | $realReturnType = new VoidType(); |
67: | } |
68: | if ($name === '__tostring') { |
69: | $realReturnType = new StringType(); |
70: | } |
71: | if ($name === '__isset') { |
72: | $realReturnType = new BooleanType(); |
73: | } |
74: | if ($name === '__sleep') { |
75: | $realReturnType = new ArrayType(new IntegerType(), new StringType()); |
76: | } |
77: | if ($name === '__set_state') { |
78: | $realReturnType = TypeCombinator::intersect(new ObjectWithoutClassType(), $realReturnType); |
79: | } |
80: | if ($name === '__set') { |
81: | $realReturnType = new VoidType(); |
82: | } |
83: | |
84: | if ($name === '__debuginfo') { |
85: | $realReturnType = TypeCombinator::intersect(TypeCombinator::addNull( |
86: | new ArrayType(new MixedType(true), new MixedType(true)), |
87: | ), $realReturnType); |
88: | } |
89: | |
90: | if ($name === '__unserialize') { |
91: | $realReturnType = new VoidType(); |
92: | } |
93: | if ($name === '__serialize') { |
94: | $realReturnType = new ArrayType(new MixedType(true), new MixedType(true)); |
95: | } |
96: | |
97: | parent::__construct( |
98: | $classMethod, |
99: | $fileName, |
100: | $templateTypeMap, |
101: | $realParameterTypes, |
102: | $phpDocParameterTypes, |
103: | $realParameterDefaultValues, |
104: | $realReturnType, |
105: | $phpDocReturnType, |
106: | $throwType, |
107: | $deprecatedDescription, |
108: | $isDeprecated, |
109: | $isInternal, |
110: | $isPure, |
111: | $acceptsNamedArguments, |
112: | $assertions, |
113: | $phpDocComment, |
114: | $parameterOutTypes, |
115: | $immediatelyInvokedCallableParameters, |
116: | $phpDocClosureThisTypeParameters, |
117: | ); |
118: | } |
119: | |
120: | public function getDeclaringClass(): ClassReflection |
121: | { |
122: | return $this->declaringClass; |
123: | } |
124: | |
125: | public function getPrototype(): ClassMemberReflection |
126: | { |
127: | try { |
128: | return $this->declaringClass->getNativeMethod($this->getClassMethod()->name->name)->getPrototype(); |
129: | } catch (MissingMethodFromReflectionException) { |
130: | return $this; |
131: | } |
132: | } |
133: | |
134: | private function getClassMethod(): ClassMethod |
135: | { |
136: | |
137: | $functionLike = $this->getFunctionLike(); |
138: | return $functionLike; |
139: | } |
140: | |
141: | public function isStatic(): bool |
142: | { |
143: | return $this->getClassMethod()->isStatic(); |
144: | } |
145: | |
146: | public function isPrivate(): bool |
147: | { |
148: | return $this->getClassMethod()->isPrivate(); |
149: | } |
150: | |
151: | public function isPublic(): bool |
152: | { |
153: | return $this->getClassMethod()->isPublic(); |
154: | } |
155: | |
156: | public function isFinal(): TrinaryLogic |
157: | { |
158: | return TrinaryLogic::createFromBoolean($this->classMethod->isFinal() || $this->isFinal); |
159: | } |
160: | |
161: | public function isFinalByKeyword(): TrinaryLogic |
162: | { |
163: | return TrinaryLogic::createFromBoolean($this->classMethod->isFinal()); |
164: | } |
165: | |
166: | public function isBuiltin(): bool |
167: | { |
168: | return false; |
169: | } |
170: | |
171: | public function getSelfOutType(): ?Type |
172: | { |
173: | return $this->selfOutType; |
174: | } |
175: | |
176: | public function returnsByReference(): TrinaryLogic |
177: | { |
178: | return TrinaryLogic::createFromBoolean($this->getClassMethod()->returnsByRef()); |
179: | } |
180: | |
181: | public function isAbstract(): TrinaryLogic |
182: | { |
183: | return TrinaryLogic::createFromBoolean($this->getClassMethod()->isAbstract()); |
184: | } |
185: | |
186: | public function hasSideEffects(): TrinaryLogic |
187: | { |
188: | if ( |
189: | strtolower($this->getName()) !== '__construct' |
190: | && $this->getReturnType()->isVoid()->yes() |
191: | ) { |
192: | return TrinaryLogic::createYes(); |
193: | } |
194: | if ($this->isPure !== null) { |
195: | return TrinaryLogic::createFromBoolean(!$this->isPure); |
196: | } |
197: | |
198: | return TrinaryLogic::createMaybe(); |
199: | } |
200: | |
201: | } |
202: | |