PHPDoc Generics

Hi, in an enterprise project and lack of type information and some magic functions is starting to drive me crazy. I know PHPStorm has some extra PHPDoc functionality, so I'm wanting to see if this is possible.

EntityType::where(['id' => 1]) /*Returns \EntityType\EntityList (or EntityList<EntityType> if you prefer - each Entity type has its own EntityList class, which extends a global EntityList Class*/
->first() /*Returns EntityType. first() is called from the root EntityList class and returns and instance of EntityType. */

Is it possible to annotate where() and first() to allow for that kind of generic?

E.g

Users:where(['id' => 1]) //Returns \Users\EntityList
->first() //Returns instance of Users

Thanks

0
9 comments

Something like that should do:

<?php

/**
 * @template T
 */
class EntityList {
    /** @return T */
    function first() {}
}

class Users {
    /** @return EntityList<User> */
    static function where() {}
}

class User {}

$user = Users::where()->first();
0

Thank you!
The where() function is in a generic entity model, so Users::Where() and Objects::where() are the same function.

Return type EntityList<$this> doesn’t seem to work. How do you get the type from the static class calling it?

The structure is a bit more complex from digging into it, but I think that’s the issue.

EDIT: Explicitly putting <User> as the return type also doesn't work.

So the actual structure looks like this

/** @template T */
class Entities{
/** @return EntityList<Users> */
function where(){
}
}

class Users extends Entities {

}

/** @template T */
class EntityList{
/** @return T */
function first(){
}
}

$users = Users::where()->first() //PHPStorm interprets as mixed

 

0

The where() function is in a generic entity model, so Users::Where() and Objects::where() are the same function.

If you are not okay with annotating Entities::where() with /** @return EntityList<User> */, the only way is to separate the chained call with a variable, because it has to be specified what exact element the collection holds.

Like this:

<?php
/**
 * @template T
 */
class EntityList {
    /** @return T */
    function first() {}
}
abstract class Objects {
    static function where(): EntityList {}
}
class Users extends Objects {}
class User {}

/** @var EntityList<User> $where */
$where = Users::where();
$user = $where->first();

We have a feature request to support generics in @method, but it hasn't been implemented yet:
https://youtrack.jetbrains.com/issue/WI-62267

$users = Users::where()->first() //PHPStorm interprets as mixed

When I copy your code into an empty file in an empty project, $user is expectedly detected as mixed|Users, because you have a typo in the where()'s @return.

What PhpStorm version are you using? It should be at least 2021.2 to have all this supported.

0

Thanks Eugene! What was my typo there? I could not get it to return anything other than mixed when I was playing around with it today.

I’m perfectly happy to annotate where(). And with regards to getting the static class that’s calling where() should I use EntityList<$this>?

0

What was my typo there?

/** @return EntityList<Users> */ instead of /** @return EntityList<User> */. Mind the plural Users.

And with regards to getting the static class that’s calling where() should I use EntityList<$this>?

I am confused. Even if this was supported, EntityList<$this> in Users would've meant that Users::where() returns a collection of instances of Users, while in fact, it returns an EntityList of instances of User.
What am I missing?

I could not get it to return anything other than mixed when I was playing around with it today.

I've added this question to my previous reply too late, so you probably didn't see it.
What is your PhpStorm version?
If it's 2021.2 or newer, please try creating a new empty project and create a single file out of your snippet there. Is it any better?

0

If it's 2021.2 or newer, please try creating a new empty project and create a single file out of your snippet there. Is it any better?

Oh. Please never mind, something in the snippet is only supported in 2021.3, 2021.2.3 doesn't have it. Please upgrade.

0

Thanks, I thought I updated to most recent before I tried, but I only upgraded to 2021.2.3.

On 2021.3, I can get it to work when the return type is explicity cast as EntityList<User>.

However I need to get the static calling class.

I am confused. Even if this was supported, EntityList<$this> in Users would've meant that Users::where() returns a collection of instances of Users, while in fact, it returns an EntityList of instances of User.
What am I missing?

where() is a method in the parent class of Users as shown in sample above, so it can be called statically by Users or any of its siblings.

e.g

Users::where() //Returns EntityList<Users>
Occupations:where() //Returns EntityList<Occupations>
SomeOtherStuff:where() //Returns EntityList<SomeOtherStuff>

So I was hoping return type of EntityList<$this> or EntityList<self> would work, but doesn't seem to.

Just not supported at the moment? Should  I make a feature request? I'm coming from F# so I'm a very big fan of having types known 😂

0

Oh, now I see, thank you.
For some reason, initially, I assumed that Users::where() returns EntityList<User>, not EntityList<Users>, and then I was ignoring all your attempts to convince me otherwise.

Just not supported at the moment?

Unfortunately, it's not. The request is already there: https://youtrack.jetbrains.com/issue/WI-63722
Please vote for it too to get notified when there's any progress.

0

Perfect, thanks for your help! I look forward to seeing it in a future release.

0

Please sign in to leave a comment.