Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Cannot call constructor on an unknown class" when creating class from variable #8628

Closed
JoolsMcFly opened this issue Oct 28, 2022 · 8 comments

Comments

@JoolsMcFly
Copy link

Hi all.

I need to create new classes from an array of class names. I have added allowStringToStandInForClass="true" to psalm.xml but I still get the following error:

MixedMethodCall - src/MyClass.php:125:26 - Cannot call constructor on an unknown class (see https://psalm.dev/015)

Si I tried to add more context:

foreach ($classNames as $className) {
    if (!class_exists($className)) {
        continue;
    }
    $class = new $className();  // <= how can I make this not fail?
}

Adding class_exists does not do the trick. Any ideas how to fix the reported error?
Thanks!

@psalm-github-bot
Copy link

Hey @JoolsMcFly, can you reproduce the issue on https://psalm.dev ?

@orklah
Copy link
Collaborator

orklah commented Oct 28, 2022

You seem to know the constructor doesn't have parameters, so I guess you have some kind of insight into what kind of class is pushed to you there.

Psalm need to know that too because otherwise, it can't guarantee you won't get a php error for passing wrong parameters.

So if you can document your $classNames as array<class-string<SomeClassOrInterfaceWithConstructWithNoParams>>, it will solve your issue.

If you're not able to do that, it means you have an inherently unsafe construct and you'll have to suppress the issue instead of trying to fix it

@JoolsMcFly
Copy link
Author

Right. I know constructors I am dealing with have no params so should be OK for now to use ReflectionClass (which solves my problem)?

try {
    $cl = new ReflectionClass($className);
    $class = $cl->newInstance();
    if ($class instanceof SomeInterface) {
        // do something
    }
} catch (ReflectionException $e) {
}

I now need to figure out why phpstan reports Dead catch - ReflectionException is never thrown in the try block..

@orklah
Copy link
Collaborator

orklah commented Oct 28, 2022

If you know you have a SomeInterface, Psalm shouldn't complain after you check that, even without going with Reflection

About PHPStan, I'd assume they have a partial stub that doesn't document this exception

@JoolsMcFly
Copy link
Author

Thanks for your follow up.

I originally had this:

foreach ($classNames as $className) {
    if (!class_exists($className)) {
        continue;
    }
    $class = new $className();
    if ($class instanceof SomeInterface) {
        // blabla
    }
}

but I still had the error message on $class = new $className();.

@orklah
Copy link
Collaborator

orklah commented Oct 28, 2022

Something like that should work: https://psalm.dev/r/f95a929e11

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/f95a929e11
<?php

$classNames = ['aaa', 'bbb'];

foreach ($classNames as $className) {
    if (!is_a($className, SomeInterface::class, true)) {
        continue;
    }
    
    $_class = new $className();
}

interface SomeInterface{}
Psalm output (using commit 7c83878):

No issues!

@JoolsMcFly
Copy link
Author

Works like a charm. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants