30

I often give objects static methods and properties that do not require the object to be initialized. For example:

class SomeObject {
    public function __construct($object_id) {
        $this->loadProperties($object_id);
    }

    public static function getSomeStaticString() {
        return "some static string";
    }
}

Now we subclass these objects and have some sort of controller that returns an object class string under certain circumstances where the object should not yet be initialized. For example:

class SomeObjectController {
    public function getSomeObjectWithTheseProperties(array $properties) {
        if($properties[0] === "somevalue") {
            if($properties[1] === "someothervalue") {
                return SomeSubclassObject::class;
            }

            return SomeObject::class;
        }

        return NULL;
    }
}

At times I might want to call the static function SomeObject::getSomeStaticString() without actually initializing the object (because that would involve an unneeded database fetch). For instance:

$controller = new SomeObjectController;
$properties = array("somevalue", "someothervalue");
$object_class = $controller->getSomeObjectWithTheseProperties($properties);

echo $object_class::getSomeStaticString();

Question: can I somehow tell PhpStorm, preferably through phpDoc, that $object_class is a class string of a subclass of SomeObject?

If I tell my IDE it's a string, it will notify me getSomeStaticString() is an invalid method. On the other hand, if I tell my IDE it's an instance of SomeObject, it thinks I can access regular non-static methods and properties, which I can't.

4 Answers 4

23

PHPStan uses the class-string PHPDoc type for this. It has been supported by PHPStorm since 2020.3, but it seems to work properly (with all autocompletion available) as of 2021.2, when they added support for Generics.

In your case the return type annotation would be @return class-string<SomeObject>.

Sign up to request clarification or add additional context in comments.

8 Comments

FYI: PhpStorm 2020.3 will have own PHPStan/Psalm integration plugins: blog.jetbrains.com/phpstorm/2020/10/phpstorm-2020-3-eap-2 and that plugin you have referenced may no longer work with newer IDE versions (partially related comment: youtrack.jetbrains.com/issue/…)
@LazyOne 2021.3 and still not a good implementation and also not available in intellij.
@stevemoretz Which plugin in particular? Both Psalm Support and PHPStan Support plugins are marked as compatible with IntelliJ IDEA Ultimate (they are bundled with PhpStorm but need to be installed manually in IntelliJ).
@stevemoretz Yes, they do not support all the functionality yet. Tickets can be seen here
This is sad that class-string not resolving static methods for the class in PhpStorm.
|
4
/** @var SomeObject $object_class */
$object_class = $controller->getSomeObjectWithTheseProperties($properties);

enter image description here

Sorry, no other way as to tell that it's an instance of SomeObject.

... if I tell my IDE it's an instance of SomeObject, it thinks I can access regular non-static methods and properties, which I can't.

So? Just do not access non-static methods and properties.


UPDATE 2021-10-26: Since v2020.3 PhpStorm has Psalm Support and PHPStan Support plugins bundled. They support most of the syntax/types supported by those tools (you can check PhpStorm Issue Tracker here for all the tickets for those plugins (resolved and still pending)).

With those tools you can use class-string pseudo type:

5 Comments

Was afraid I'd be pointed to @var SomeObject. That's as far as I got. I feel dirty calling a string an object.
Problem is: neither PHPDoc or PhpStorm supports "this is a string that represents a class" type of typehint. If you want - use string instead of SomeObject -- feel cleaner now? Thing is -- this will not make warning to disappear. You can always suppress a warning for that line .. but that is actually a wrong way / more dirty.
Given the above conversation, I'd also recommend you add a description to that doc comment explaining that this is a reference to the class, not an actual instance. It may help a team member that is reading your code later on. My apologies for the archaeology. I'm just leaving this here in case the next person is short a couple cents. ;)
Well .. if someone just needs a warning suppression ... then he/she can use some guard logic, e.g. surround with if (method_exist(...) {...} call -- IDE understands it now. It will help with not showing a warning .. but may still not provide code completion assistance.
So how to let IDE and static analyzers know that the variable has a specific class? Could you add examples in addition to linking to documentation?
-1

You can also use this workaround:

/**
 * @return string|SomeObject
 */
public function getSomeObjectClass()
{
    return SomeObject::class;
}

3 Comments

Please add some explanation to your answer such that others can learn from it. The given method never returns an instance of SomeObject, so to me this does not look correct
@NicoHaase This answer is correct in working around this issue so you can retain type hints.
Improved modern solution without PHPDoc - function getSomeObjectClass(): string|SomeObject { ... . It works without errors and provides type hinting.
-1

If you want perfect type hinting you may create an Interface which only lists the static methods (and properties) of your class, and use that Interface as the type hint for your classname string returned from SomeObjectController::getSomeObjectWithTheseProperties().

It adds overhead to the maintenance to ensure the Interface and the classes are kept in sync, but if you need those type hints to work properly, this is the way.

1 Comment

Please share more details. How would the phpDoc annotation look like for that method?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.