UPDATE:PHP7.4现在是does support covariance and contravariance,它解决了这个问题中提出的主要问题.


我在PHP7中使用返回类型暗示时遇到了一些问题.我的理解是,暗示: self意味着您希望实现类返回自身.因此,我在接口中使用了: self来表示这一点,但当我试图实际实现接口时,我得到了兼容性错误.

以下是我遇到的问题的简单演示:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

预期yields 为:

弗莱德

我实际得到的是:

PHP致命错误:声明Foo::bar(int$baz):Foo必须与测试中的iFoo::bar(int$baz):iFoo兼容.php第7行

问题是Foo是iFoo的一个实现,所以据我所知,这个实现应该与给定的接口完全兼容.我可以通过更改接口或实现类(或两者)来修复这个问题,以按名称返回接口,而不是使用self,但我的理解是,从语义上讲,self意味着"返回您刚才调用方法的类的实例".因此,将其更改为接口在理论上意味着我可以返回实现接口的任何对象的实例,而我的意图是调用的实例将被返回.

这是PHP中的疏忽还是故意的设计决定?如果是前者,有没有可能在PHP7.1中修复它?如果不是,那么什么是正确的返回方式,暗示您的接口希望您返回刚刚调用链接方法的实例?

推荐答案

编辑注:下面的答案已经过时了.如php PHP7.4.0,以下内容完全合法:

<?php
Interface I{
    public static function init(?string $url): self;
}
class C implements I{
    public static function init(?string $url): self{
        return new self();
    }
}
$o = C::init("foo");
var_dump($o);

原始答案:

self不是指实例,而是指当前类.接口无法指定必须返回相同的instance——以您try 的方式使用self只会强制返回的实例属于同一类.

也就是说,PHP中的返回类型声明必须是不变的,而您try 的是协变的.

使用self相当于:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

这是不允许的.


Return Type Declarations RFC强有this to say项:

继承期间声明的返回类型的强制是不变的;这意味着,当子类型重写父方法时,子类型的返回类型必须与父类型完全匹配,并且不能忽略.如果父级未声明返回类型,则允许子级声明返回类型.

...

该RFC最初提出了协变返回类型,但由于一些问题而改为不变.在future 的某个时刻,可以添加协变回报类型.


至少目前你能做的最好的事情是:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

Php相关问答推荐

try 修改这个PHP代码如果语句在WordPress""

在保留唯一键值对的情况下高效合并嵌套数组

累加平面数组中数字子集的所有组合,以生成组合及其乘积的二维数组

保存PHP条带以备将来使用,不显示用户界面

Laravel数据表在一个视图中传递两个实例

在WooCommerce中设置带有国家/地区和年份前缀的顺序序号

使用ESI的Symfony Sulu清漆

基于自定义域的每个购物车项目的WooCommerce定制数量输入步骤

如何使用定制的StateProvider设置JsonLd@上下文?

使用CODIGNITER3中的OR和AND子句进行查询

在WooCommerce Admin产品列表自定义列中显示来自元数据的统计数据

Woocommerce 仅在前端为登录用户提供价格折扣

PHP 支持 APNG 文件吗?

通过添加计数器修改文本区域表单中的重复值

计算添加到购物车的点击次数并将其显示在 WooCommerce 管理产品列表中

Google Sheets PHP API - 添加多个选项卡时出现问题

如何从此字符串创建 Carbon 对象:2022-06-21T05:52:46.648533678Z?

PHP 中是否有 is_closure() 函数?

为什么在 phpunit 的开头测试 PHP_VERSION?

如何在 php/laravel 中获取元素在数组中的位置