Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7145491

Browse files
Fix calling non-static method without an object
1 parent b01e592 commit 7145491

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

‎src/Type/ReflectionHelperGetPrivateMethodInvokerReturnTypeExtension.php‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
final class ReflectionHelperGetPrivateMethodInvokerReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
3030
{
31+
private const OBJECT_AS_STRING_CONTEXT = 0;
32+
private const OBJECT_AS_OBJECT_CONTEXT = 1;
33+
3134
/**
3235
* @param class-string $class
3336
*/
@@ -53,21 +56,25 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
5356
return null;
5457
}
5558

56-
$objectType = $scope->getType($args[0]->value)->getObjectTypeOrClassStringObjectType();
59+
$objectType = $scope->getType($args[0]->value);
5760
$methodType = $scope->getType($args[1]->value);
5861

59-
if (! $objectType->isObject()->yes()) {
60-
return new NeverType(true);
61-
}
62-
6362
return TypeTraverser::map($objectType, static function (Type $type, callable $traverse) use ($methodType, $scope, $args, $methodReflection): Type {
6463
if ($type instanceof UnionType || $type instanceof IntersectionType) {
6564
return $traverse($type);
6665
}
6766

67+
$context = self::OBJECT_AS_OBJECT_CONTEXT;
68+
69+
if ($type->isString()->yes()) {
70+
$context = self::OBJECT_AS_STRING_CONTEXT;
71+
}
72+
6873
$closures = [];
6974

70-
foreach ($type->getObjectClassReflections() as $classReflection) {
75+
$objectType = $type->getObjectTypeOrClassStringObjectType();
76+
77+
foreach ($objectType->getObjectClassReflections() as $classReflection) {
7178
foreach ($methodType->getConstantStrings() as $methodStringType) {
7279
$methodName = $methodStringType->getValue();
7380

@@ -86,7 +93,15 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
8693
$invokedMethodReflection->getNamedArgumentsVariants(),
8794
);
8895

89-
$returnType = strtolower($methodName) === '__construct' ? $type : $parametersAcceptor->getReturnType();
96+
if (! $invokedMethodReflection->isStatic() && $context === self::OBJECT_AS_STRING_CONTEXT) {
97+
// ReflectionException: Trying to invoke non static method FQCN::method() without an object
98+
$returnType = new NeverType(true);
99+
} elseif (strtolower($methodName) === '__construct') {
100+
// Do not use void as the return type of __construct
101+
$returnType = $objectType;
102+
} else {
103+
$returnType = $parametersAcceptor->getReturnType();
104+
}
90105

91106
$closures[] = new ClosureType(
92107
$parametersAcceptor->getParameters(),
@@ -99,6 +114,10 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
99114
}
100115

101116
if ($closures === []) {
117+
if (! $objectType->isObject()->yes()) {
118+
return new NeverType(true);
119+
}
120+
102121
return ParametersAcceptorSelector::selectFromArgs(
103122
$scope,
104123
$args,

‎tests/Type/data/reflection-helper.php‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,26 +64,26 @@ public function testObjectAsObjectType(): void
6464

6565
public function testClassStringAsObjectType(): void
6666
{
67-
assertType('Closure(): void', self::getPrivateMethodInvoker(self::class, 'testOnFirstClassCallable'));
67+
assertType('Closure(): never', self::getPrivateMethodInvoker(self::class, 'testOnFirstClassCallable'));
6868

6969
$object = ModelReturnTypeTransformVisitor::class;
70-
assertType('Closure(PhpParser\Node): null', self::getPrivateMethodInvoker($object, 'enterNode'));
70+
assertType('Closure(PhpParser\Node): never', self::getPrivateMethodInvoker($object, 'enterNode'));
7171
assertType(
72-
'Closure(array<PhpParser\Node>): (array<PhpParser\Node>|null)',
72+
'Closure(array<PhpParser\Node>): never',
7373
self::getPrivateMethodInvoker($object, 'afterTraverse'),
7474
);
7575

7676
$object = FactoriesFunctionReturnTypeExtension::class;
7777
assertType(
78-
'Closure(CodeIgniter\PHPStan\Type\FactoriesReturnTypeHelper): CodeIgniter\PHPStan\Type\FactoriesFunctionReturnTypeExtension',
78+
'Closure(CodeIgniter\PHPStan\Type\FactoriesReturnTypeHelper): never',
7979
self::getPrivateMethodInvoker($object, '__construct'),
8080
);
8181
assertType(
82-
'Closure(PHPStan\Reflection\FunctionReflection): bool',
82+
'Closure(PHPStan\Reflection\FunctionReflection): never',
8383
self::getPrivateMethodInvoker($object, 'isFunctionSupported'),
8484
);
8585
assertType(
86-
'Closure(PHPStan\Reflection\FunctionReflection, PhpParser\Node\Expr\FuncCall, PHPStan\Analyser\Scope): (PHPStan\Type\Type|null)',
86+
'Closure(PHPStan\Reflection\FunctionReflection, PhpParser\Node\Expr\FuncCall, PHPStan\Analyser\Scope): never',
8787
self::getPrivateMethodInvoker($object, 'getTypeFromFunctionCall'),
8888
);
8989
}
@@ -132,7 +132,7 @@ public function testOnClassString(string $object): void
132132
public function testOnGenericClassString(string $class): void
133133
{
134134
assertType(
135-
'Closure(Psr\Log\LoggerInterface, CodeIgniter\CLI\Commands): CodeIgniter\Commands\Utilities\ConfigCheck',
135+
'Closure(Psr\Log\LoggerInterface, CodeIgniter\CLI\Commands): never',
136136
self::getPrivateMethodInvoker($class, '__construct'),
137137
);
138138
}
@@ -164,7 +164,7 @@ public function testOnUnionOfObjects(object|string $object): void
164164
assertType(
165165
sprintf(
166166
'%s|%s',
167-
'(Closure(CodeIgniter\PHPStan\Type\ServicesReturnTypeHelper): CodeIgniter\PHPStan\Type\ServicesFunctionReturnTypeExtension)',
167+
'(Closure(CodeIgniter\PHPStan\Type\ServicesReturnTypeHelper): never)',
168168
'(Closure(non-empty-string): CodeIgniter\PHPStan\Tests\Fixtures\Type\ReflectionHelperGetPrivateMethodInvokerTest)',
169169
),
170170
self::getPrivateMethodInvoker($object, '__construct'),
@@ -177,7 +177,7 @@ public function testOnUnionOfObjects(object|string $object): void
177177
public function testOnUnionOfStringObjectsWithOneNonClass(string $object): void
178178
{
179179
assertType(
180-
'*NEVER*',
180+
'Closure(Psr\Log\LoggerInterface, CodeIgniter\CLI\Commands): never',
181181
self::getPrivateMethodInvoker($object, '__construct'),
182182
);
183183
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /