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: | class PhpMethodFromParserNodeReflection extends PhpFunctionFromParserNodeReflection implements ExtendedMethodReflection |
30: | { |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | public function __construct( |
40: | private ClassReflection $declaringClass, |
41: | 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: | 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: | $isFinal || $classMethod->isFinal(), |
111: | $isPure, |
112: | $acceptsNamedArguments, |
113: | $assertions, |
114: | $phpDocComment, |
115: | $parameterOutTypes, |
116: | $immediatelyInvokedCallableParameters, |
117: | $phpDocClosureThisTypeParameters, |
118: | ); |
119: | } |
120: | |
121: | public function getDeclaringClass(): ClassReflection |
122: | { |
123: | return $this->declaringClass; |
124: | } |
125: | |
126: | public function getPrototype(): ClassMemberReflection |
127: | { |
128: | try { |
129: | return $this->declaringClass->getNativeMethod($this->getClassMethod()->name->name)->getPrototype(); |
130: | } catch (MissingMethodFromReflectionException) { |
131: | return $this; |
132: | } |
133: | } |
134: | |
135: | private function getClassMethod(): ClassMethod |
136: | { |
137: | |
138: | $functionLike = $this->getFunctionLike(); |
139: | return $functionLike; |
140: | } |
141: | |
142: | public function isStatic(): bool |
143: | { |
144: | return $this->getClassMethod()->isStatic(); |
145: | } |
146: | |
147: | public function isPrivate(): bool |
148: | { |
149: | return $this->getClassMethod()->isPrivate(); |
150: | } |
151: | |
152: | public function isPublic(): bool |
153: | { |
154: | return $this->getClassMethod()->isPublic(); |
155: | } |
156: | |
157: | public function isBuiltin(): bool |
158: | { |
159: | return false; |
160: | } |
161: | |
162: | public function getSelfOutType(): ?Type |
163: | { |
164: | return $this->selfOutType; |
165: | } |
166: | |
167: | public function returnsByReference(): TrinaryLogic |
168: | { |
169: | return TrinaryLogic::createFromBoolean($this->getClassMethod()->returnsByRef()); |
170: | } |
171: | |
172: | public function isAbstract(): TrinaryLogic |
173: | { |
174: | return TrinaryLogic::createFromBoolean($this->getClassMethod()->isAbstract()); |
175: | } |
176: | |
177: | public function hasSideEffects(): TrinaryLogic |
178: | { |
179: | if ( |
180: | strtolower($this->getName()) !== '__construct' |
181: | && $this->getReturnType()->isVoid()->yes() |
182: | ) { |
183: | return TrinaryLogic::createYes(); |
184: | } |
185: | if ($this->isPure !== null) { |
186: | return TrinaryLogic::createFromBoolean(!$this->isPure); |
187: | } |
188: | |
189: | return TrinaryLogic::createMaybe(); |
190: | } |
191: | |
192: | } |
193: | |