Description
Bug report
I noticed that the following code produces four warnings:
<?php
use function PHPStan\Testing\assertType;
interface ResourceInterface {}
class Person implements ResourceInterface
{
public function getName(): string { return 'Name'; }
}
class Account implements ResourceInterface
{
public function getMail(): string { return 'Mail'; }
}
function foo(?ResourceInterface $object = null): void
{
switch ($object::class) {
case Person::class:
assertType(Person::class, $object);
echo $object->getName();
break;
case Account::class:
assertType(Account::class, $object);
echo $object->getMail();
break;
}
}
Line | Error | Type |
---|---|---|
21 | Expected type Person, actual: ResourceInterface|null | Non-ignorable |
22 | Call to an undefined method ResourceInterface::getName(). | method.notFound |
25 | Expected type Account, actual: ResourceInterface|null | Non-ignorable |
26 | Call to an undefined method ResourceInterface::getMail(). | method.notFound |
Code snippet that reproduces the problem
https://phpstan.org/r/f9c2c069-07b2-46a3-b83c-837205e6581f
Expected output
The errors surprised me, as the type is actually checked and, hence, the code reported by PhpStan will not produce any errors during runtime. The feature of checking types by switch
was covered in #745, #4896.
I found, however, that the code above seems to be related to $object
being nullable. If you change the method signature from ?ResourceInterface $object = null
to ResourceInterface $object
, the errors are suddenly gone!
Next, I discovered that accessing ::class
on null actually throws an error in PHP 8.4.7 (and probably all PHP 8 versions):
$ php -r "\$foo = null; var_dump(\$foo::class);"
PHP Fatal error: Uncaught TypeError: Cannot use "::class" on null in Command line code:1
Stack trace:
#0 {main}
thrown in Command line code on line 1
So PhpStan is actually right in reporting something, though I think the reported errors are incorect. In my opinion, PhpStan should complain about accessing ::class
on nullable types.
Did PHPStan help you today? Did it make you happy in any way?
No response
Activity
staabm commentedon May 23, 2025
You can use
get_class
until we are able to narrow the types better in your examplehttps://phpstan.org/r/6d9edb50-77bf-4997-b737-ac4a2251026d
Your analysis looks correct to me for the cases given
driehle commentedon May 23, 2025
Indeed, using
get_class()
doesn't throw errors in PhpStan as::class
does. However, both throw a TypeError in PHP when called on a null value:While I said the issues is PhpStan not complaining on accessing
::class
on a null value, this is not fully correct. PhpStan only complains so, if the type is null and nothing else I think. See this example, where it complains about the premier two, but not about the latter two:https://phpstan.org/r/6c8b65e4-619a-47a6-8db2-6f1dd101e75e
[-]Accessing ::class on null objects does not lead to an immediate warning[/-][+]Accessing ::class or calling get_class() on nullable types does not lead to a warning[/+]VincentLanglet commentedon May 24, 2025
@driehle You're doing your tests on level 6.
Union types (except null) are reported level 7
Union types with null are reporter level 8
This is a level 8 error https://phpstan.org/r/349f2d3d-a0d6-4f93-9837-1a0e20da486d
driehle commentedon May 24, 2025
Ok, I think I got that. So it is then just a difference in how
switch($object::class)
andswitch(get_class($ object))
are interpreted? But, then, why does anif (null === $object) return;
before theswitch
solve the error?VincentLanglet commentedon May 24, 2025
I tried a fix phpstan/phpstan-src#4011