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\ObjectWithoutClassType;
18: use PHPStan\Type\StringType;
19: use PHPStan\Type\Type;
20: use PHPStan\Type\TypeCombinator;
21: use PHPStan\Type\VoidType;
22: use function strtolower;
23:
24: /**
25: * @api
26: */
27: class PhpMethodFromParserNodeReflection extends PhpFunctionFromParserNodeReflection implements ExtendedMethodReflection
28: {
29:
30: /**
31: * @param Type[] $realParameterTypes
32: * @param Type[] $phpDocParameterTypes
33: * @param Type[] $realParameterDefaultValues
34: */
35: public function __construct(
36: private ClassReflection $declaringClass,
37: ClassMethod $classMethod,
38: string $fileName,
39: TemplateTypeMap $templateTypeMap,
40: array $realParameterTypes,
41: array $phpDocParameterTypes,
42: array $realParameterDefaultValues,
43: Type $realReturnType,
44: ?Type $phpDocReturnType,
45: ?Type $throwType,
46: ?string $deprecatedDescription,
47: bool $isDeprecated,
48: bool $isInternal,
49: bool $isFinal,
50: ?bool $isPure,
51: bool $acceptsNamedArguments,
52: Assertions $assertions,
53: private ?Type $selfOutType,
54: ?string $phpDocComment,
55: array $parameterOutTypes,
56: )
57: {
58: $name = strtolower($classMethod->name->name);
59: if (
60: $name === '__construct'
61: || $name === '__destruct'
62: || $name === '__unset'
63: || $name === '__wakeup'
64: || $name === '__clone'
65: ) {
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:
81: parent::__construct(
82: $classMethod,
83: $fileName,
84: $templateTypeMap,
85: $realParameterTypes,
86: $phpDocParameterTypes,
87: $realParameterDefaultValues,
88: $realReturnType,
89: $phpDocReturnType,
90: $throwType,
91: $deprecatedDescription,
92: $isDeprecated,
93: $isInternal,
94: $isFinal || $classMethod->isFinal(),
95: $isPure,
96: $acceptsNamedArguments,
97: $assertions,
98: $phpDocComment,
99: $parameterOutTypes,
100: );
101: }
102:
103: public function getDeclaringClass(): ClassReflection
104: {
105: return $this->declaringClass;
106: }
107:
108: public function getPrototype(): ClassMemberReflection
109: {
110: try {
111: return $this->declaringClass->getNativeMethod($this->getClassMethod()->name->name)->getPrototype();
112: } catch (MissingMethodFromReflectionException) {
113: return $this;
114: }
115: }
116:
117: private function getClassMethod(): ClassMethod
118: {
119: /** @var Node\Stmt\ClassMethod $functionLike */
120: $functionLike = $this->getFunctionLike();
121: return $functionLike;
122: }
123:
124: public function isStatic(): bool
125: {
126: return $this->getClassMethod()->isStatic();
127: }
128:
129: public function isPrivate(): bool
130: {
131: return $this->getClassMethod()->isPrivate();
132: }
133:
134: public function isPublic(): bool
135: {
136: return $this->getClassMethod()->isPublic();
137: }
138:
139: public function isBuiltin(): bool
140: {
141: return false;
142: }
143:
144: public function getSelfOutType(): ?Type
145: {
146: return $this->selfOutType;
147: }
148:
149: public function returnsByReference(): TrinaryLogic
150: {
151: return TrinaryLogic::createFromBoolean($this->getClassMethod()->returnsByRef());
152: }
153:
154: }
155: