假设你有3个这样的模型
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';
}
那么你有一个函数返回3个模型,像这样
protected function generateModels(): array
{
$user = new User();
$car = new Car();
$house = new House();
return [$user, $car, $house]
}
那么你有一些像这样的测试
/**
* @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
}
那么你需要如何输入提示函数generateModels()
, PHPstan理解它可以是多个模型?因为array<int, mixed>
不起作用,array<int, AbstractModel>
也不起作用,因为它会像property $name doesnt exist on AbstractModel
一样抱怨,第三个像array<int, User|Car|House>
一样似乎也不起作用,因为你会得到同样的错误,然后函数说Car|House
类型现在是允许的。那么我怎样才能正确地输入hint呢?
PHPStan level 9
当一个方法返回array<int,User|Car|House>
时,这意味着该方法返回这些类的实例数组。根据该注释,您可以获得[User,Car]
、[Car,House]
或仅[Car,Car,Car]
(或任何其他组合)。这就是PHPStan抱怨这个的原因:它试图告诉你将来可能会有错误。虽然您(作为作者)知道此时第一个位置包含User
,但代码不知道它,因此它假设第一个位置可能不是User
,而是Car
或House
,它们不像User
那样包含属性$name
。为了避免这种可能的错误,您可以使用instanceof
来检查类。如果您使用$res[0] instanceof User
, PHPStan(和代码本身)将知道数组中的第一项是User
的实例,这意味着您可以毫无顾虑地获得$name
属性。
$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
}
但是如果你知道数组内部类型的顺序(例如,第一个是User
,第二个是Car
,最后是House
),你可以使用数组形状用于指定单个键的类型。
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]
}
}