PHP 7中的抽象函数参数类型提示覆盖

时间:2017-12-13 20:59:52

标签: php type-hinting

是否可以使用子类中的函数覆盖PHP 7中的抽象函数,该函数将缩小接受的参数类型?

详细阐述 - 让我们假设我有一个抽象的方法:

abstract public function setTarget( AbstractDeliveryTarget $deliveryTarget ): Delivery;

现在在一个子类中,我想覆盖此方法的签名,以便它只接受特定的参数类型(假设EmailDeliveryTarget扩展AbstractDeliveryTarget):

public function setTarget( EmailDeliveryTarget $emailDeliveryTarget ): Delivery;

然后,PHP解释器会抱怨抽象方法的签名不能在子类中更改。那么,是否有一种方法可以在一个儿童类中实现类型安全,而不是某些方法体型护卫?

2 个答案:

答案 0 :(得分:5)

这不是技术限制;你要求的东西对OOP的原则没有意义。

你的抽象类是合同;让我们定义基类:

class AbstractDeliverer {
    abstract public function setTarget(
        AbstractDeliveryTarget $deliveryTarget
    ): Delivery;
}

和一些交付目标

class AbstractDeliveryTarget {}
class EmailDeliveryTarget extends AbstractDeliveryTarget {}
class SMSDeliveryTarget extends AbstractDeliveryTarget {}

然后你可以把它写在其他地方:

function deliverAll(AbstractDeliverer $d) {
    $e = new EmailDeliveryTarget;
    $d->setTarget($e);

    $s = new SMSDeliveryTarget;
    $d->setTarget($s);
}

因为我们知道$dAbstractDeliverer,所以我们知道将$e$s传递给它应该都有效。当我们确保输入属于那种类型时,这是我们保证的合同。

现在让我们看看如果按照您想要的方式扩展会发生什么:

class EmailOnlyDeliverer extends AbstractDeliverer {
    public function setTarget(
        EmailDeliveryTarget $emailDeliveryTarget
    ): Delivery { 
        /* ... */
    }
}
$emailonly = new EmailOnlyDeliverer;

我们知道$e instanceOf AbstractDeliverertrue,因为我们已经继承了,所以我们知道我们可以安全地调用我们的deliverAll方法:

deliverAll($emailonly);

该函数的第一部分很好,并且将有效地运行:

$e = new EmailDeliveryTarget;
$emailonly->setTarget($e);

但接下来我们点击了这一部分:

$s = new SMSDeliveryTarget;
$emailonly->setTarget($s);

糟糕!致命错误!但AbstractDeliverer上的合同告诉我们这是正确的价值!出了什么问题?

规则是一个子类必须接受父类可以接受的所有输入,但是如果它想要它可以接受其他输入,这被称为" contravariance" 。 (返回类型是"协变":子类必须永远不会返回一个不能从父类返回的值,但可以做出更强的承诺,它只会返回一个子类那些价值观;我会让你自己想出一个这样的例子。

答案 1 :(得分:0)

看起来没有漂亮的解决方案,所以我们可以尝试使用接口。

<?php
interface DeliveryTarget {}
class AbstractDeliveryTarget implements  DeliveryTarget {

}

class EmailDeliveryTarget   extends  AbstractDeliveryTarget{

}


abstract class Q {
     abstract protected function action(DeliveryTarget $class);
}

class Y extends Q {
    protected function action(DeliveryTarget $class){}
}

OR

interface DeliveryTargetAction {}


class AbstractDeliveryTarget {

}

class EmailDeliveryTarget   extends  AbstractDeliveryTarget implements DeliveryTargetAction {

}


abstract class Q {
     abstract protected function action(DeliveryTargetAction $class);
}

class Y extends Q {
    protected function action(DeliveryTargetAction $class){}
}

取决于你想做什么。