PHP 8 方法覆盖来自同一 BaseClass 的不同类型

PHP 8 Method overriding with different types from the same BaseClass

我的项目 运行 在 PHP 7.X 上运行良好,在升级到 PHP 8 后,我遇到了以下问题,我不知道如何解决这个问题.

我有以下(简化)情况:

<?php

class Vehicle
{
    //...
}

class Car extends Vehicle
{
    //...
}


class VehicleOutputMaker
{
    public function output(Vehicle $entity)
    {
        
    }
}


class CarOutputMaker extends VehicleOutputMaker
{
    //THROWS EXCEPTION
    public function output(Car $entity)
    {
        parent::output($entity);
    }
}

我的整个项目是这样的 运行 但是因为 PHP 8 我得到了行

的异常

public function output(Car $entity)

带有“致命错误:*** 的声明必须与 *** 兼容”

它与 PHP 7 完美配合!因为“Car”也是一种交通工具。

有人知道如何解决这个问题吗?

谢谢!

为什么不将 Car 的类型提示更改为 Vehicle,因为这样父 class' 方法签名得到尊重。

然后,如果您想执行 Car 的特定行为,则必须在 CarOutputMaker 的输出方法中输入检查并将 Vehicle 类型转换为 Car。

class CarOutputMaker extends VehicleOutputMaker
{
    //THROWS EXCEPTION
    public function output(Vehicle $entity)
    {
        parent::output($entity);
    }
}

您收到警告并现在出现致命错误的原因是您的代码不遵守子类型中方法参数类型的 Liskov substitution principle (LSP) standard requirement of contravariance

考虑到这一点,问题是虽然子class 中的方法可以扩展参数类型的范围,但它必须接受父class 接受的所有参数类型。

你的子class CarOutputMaker 打破了这个规则,它的方法 output() 接受类型 Car 的参数(Vehicle 的子类型),但不是在其父方法 VehicleOutputMaker::output().

中声明的超类型 Vehicle 的参数

因此这在 PHP7 中有效:

class VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
    }  
}  
  
class CarOutputMaker extends VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
        parent::output($entity);  
    }  
}

值得注意的是,由于您使用的是 PHP8,因此您可以使用 union types 在子 class 中对两种类型进行类型提示:Vehicle|Car.

class VehicleOutputMaker  
{  
    public function output(Vehicle $entity)  
    {  
    }  
}  
  
class CarOutputMaker extends VehicleOutputMaker  
{  
    public function output(Vehicle|Car $entity)  
    {  
        parent::output($entity);  
    }  
}