Home > database >  return object models in array with PHPStan
return object models in array with PHPStan

Time:01-16

Lets say you have 3 models like this

class User extends AbstractModel {
     protected string $name = 'User';
} 

class Car extends AbstractModel {
     protected int $weels = 4;
} 

class House extends AbstractModel {
     protected string $address = 'Some Street 26a';
} 

then you have a fuctions that returns the 3 models like this

protected function generateModels(): array
{
     $user  = new User();
     $car   = new Car();
     $house = new House();

     return [$user, $car, $house]
}

then you have some tests like this

/**
 * @test
 */
public fuction this_is_some_random_test(): void
{
     [
         $user,
         $car,
         $house,
     ] = $this->generateModels();

     $user->name;
     $address->weels;
     $house->address;

     $result1 = some_function_require_user_model($user);

     //some assertions
}

So how do you need to typehint the functions generateModels() that PHPstan understands that It can be multiple models? cause array<int, mixed> doesnt works array<int, AbstractModel> also not cause it will complain like property $name doesnt exist on AbstractModel and the third is like array<int, User|Car|House> what also doenst seem the works cause you will get the same errors and then the functions says that the Car|House types are now allowed. So how can I type hint it propperly?

PHPStan level 9

CodePudding user response:

When a method returns array<int,User|Car|House> it means that the method returns an array of instances of those classes. According to this annotation you can get [User,Car], [Car,House] or just [Car,Car,Car] (or any other combination). This is why PHPStan complains about this: it's trying to tell you that there might be possible error in the future. Although you (as an author) know that the first position contains User at this moment, the code doesn't know it so it assumes that the first position might not be User but also Car or House which don't contain property $name as User does. For avoiding this possible error you can use instanceof for checking the class. If you use $res[0] instanceof User PHPStan (and the code itself) will know that the first item in array is an instance of User meaning you can get the $name property without any worry.

$res = $this->generateModels(); // $res is array<User|Car|House>

$item1 = $res[0]; // $item1 is User|Car|House

echo $item1->name; // Error: name has only User (but $item1 is User, Car or House)

if ($item1 instanceof User) {
    echo $item1->name; // OK: The code knows $item1 is 100% User
} 

But if you know the order of the types inside the array (e.g. first is User, second is Car and last is House), you can use array shapes for speciffying types of individual keys.

class ModelGenerator
{
    /**
     * @return array{0: User, 1: Car, 2: House}
     */
    protected function generateModels(): array
    {
         $user  = new User();
         $car   = new Car();
         $house = new House();
    
         return [$user, $car, $house]
    }
}
  • Related