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]
}
}