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