Phpstan: Call to an unknown static method in class_exists() block

Created on 25 May 2017  Â·  21Comments  Â·  Source: phpstan/phpstan

Bug

If you analyze the following code with PHPStan (class '\Cake\Codeception\Console\Installer' does not exist):

if (class_exists('\Cake\Codeception\Console\Installer')) {
    \Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
}

It will report the following error.

Call to static method customizeCodeceptionBinary() on an unknown class Cake\Codeception\Console\Installer.

Expected

Call to an unknown static method and class_exists() should not throw a violation if the call is wrapped with class_exists() .

feature-request

Most helpful comment

All 21 comments

Yeah, I'm getting lots of false positives with this too.

Issue is also referenced here: https://github.com/phpstan/phpstan/issues/323

Glad to hear that there is some progress on this.

Any progress on this?

If there was an update, it would be mentioned here.

My opinion is that you should always make all the classes defined when the static analysis runs.

My issue was actually this:
https://github.com/phpstan/phpstan/issues/2533

And was solved by a correct configuration.

My opinion is that you should always make all the classes defined when the static analysis runs.

Bump here: this is sometimes not possible; in my case, I'm using that construct to create some code execution patch for backward compatibility. So the code is:

if (class_exists(\Some\Old\Deprecated\Class::class)) {
    return new \Some\Old\Deprecated\Class();
} else {
    return new \Some\Newer\Class();
}

Bump here: this is sometimes not possible

It's always possible. You can include the class only for static analysis purposes. That way you'll know that the backwards compatibility code also works.

It's a class from a different version of a Symfony component, I cannot install it twice; are you suggesting me to write down a stub?

Yeah, something like that.

But I can't redeclare a class with the same name but a different signature, that's my case: the use case for me is the Symfony's EventDispatcherInterface:

# https://github.com/symfony/symfony/blob/2baa8128ad5114c7df8b78e6d2fe8c572ac93758/src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php#L34
    public function dispatch($eventName, Event $event = null);
# vs https://github.com/symfony/symfony/blob/000ffb29e1cec73cafdf01d21924c61d75aecf20/src/Symfony/Contracts/EventDispatcher/EventDispatcherInterface.php#L30
# from Symfony\Contracts\EventDispatcher\EventDispatcherInterface
    public function dispatch(object $event, string $eventName = null): object;

[EDIT] also the main problem is that in my case I get a child process crash, not an error (that I could ignore):

 -- ---------------------------------------------------------------------------------------------
     Error                                                                                       
 -- ---------------------------------------------------------------------------------------------
     Child process error (exit code 255): Fatal error: Declaration of
     Paraunit\Lifecycle\ForwardCompatEventDispatcher::dispatch($event,
     ?Paraunit\Lifecycle\AbstractEvent $dummyEventArgument = NULL) must be compatible with
     Symfony\Contracts\EventDispatcher\EventDispatcherInterface::dispatch(object $event, ?string
     $eventName = NULL): object in
     /home/paraunit/projects/src/Lifecycle/ForwardCompatEventDispatcher.php on line 10

 -- ---------------------------------------------------------------------------------------------

@Jean85 So you're talking about something different - the class exists but it's different. So your problem isn't related to class_exists?

I'm sorry, I mixed up another issue that I was having in the same project. For class_exists, the issue is the Symfony\Component\EventDispatcher\Event class, which is deprecated and removed in later Symfony versions.

So that would be fixable with a conditional stub which would be declared if ! class_exists.
How would I include those? With PHPStan's bootrap option? Or is there another way?

Best to put it in autoload_files in phpstan.neon.

The purpose of class_exists function is to ... check if the class exists...
If it doesn't, the analyzer should not report false positive.

You can include the class only for static analysis purposes.

That way I'm writing an unnecessary code to make a tool work properly.

Adding the class has the value of correct analysis of the code that uses the class.

In that case, it should be two independent test scenarios – with the old class and with the new class (different library versions installed for example).

Yes, which you can do as I've written here https://github.com/phpstan/phpstan/issues/3169#issuecomment-614178063

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
BTW: Did you know that PHPStan now has a brand new website with a user guide, guide to writing analyser-friendly PHP code, and a guide to developing PHPStan extensions? Visit phpstan.org today!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dereuromark picture dereuromark  Â·  3Comments

pretzlaw picture pretzlaw  Â·  3Comments

dingo-d picture dingo-d  Â·  3Comments

gnutix picture gnutix  Â·  3Comments

llissssss picture llissssss  Â·  3Comments