Reference: Return type of ... should either be compatible with ..., or the #[\ReturnTypeWillChange] attribute should be used

回答 2 浏览 1.7万 2022-02-15

在PHP8.1中,以下代码,在以前的版本中是有效的。

class Example implements Countable {
    public function count() {
        return 42;
    }
}

引发一个弃用通知。

Deprecated: Return type of Example::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice

这是什么意思,我应该如何解决这个问题?

IMSoP 提问于2022-02-15
2 个回答
#1楼 已采纳
得票数 34

背景:返回类型和协方差

自PHP7.0以来,可以指定一个函数或方法的返回类型,例如function example(): string表示一个返回字符串的函数。这就形成了一个其他代码可以依赖的契约

例如,这个类承诺,getList方法将返回某种Iterator的结果。

class Base {
    public function getList(): Iterator {
       // ...
    }
}

编写调用代码时可以知道,如果$foo instanceOf Base为真,那么$foo->getList() instanceOf Iterator也将为真。

如果你扩展了这个类,你可以指定相同的返回类型,或者更具体的返回类型(一个被称为"协方差"的规则),而调用代码的假设仍将是真实的。

class SubClass extends Base {
    public function getList(): DirectoryIterator {
        // ...
    }
}
$foo = new SubClass;
var_dump($foo instanceOf Base); // true
var_dump($foo->getList() instanceOf Iterator); // true

但是如果你指定了一个不同的返回类型,或者根本没有返回类型,这个假设就会被打破,所以PHP不允许你这样做。

class NotPossible extends Base {
    public function getList(): bool {
        return false;
    }
}
// Fatal error: Declaration of NotPossible::getList(): bool must be compatible with Base::getList(): Iterator
// If the error didn't happen...
$foo = new NotPossible;
var_dump($foo instanceOf Base); // would be true
var_dump($foo->getList() instanceOf Iterator); // would be false!

向后的兼容性和弃用

如果你给一个现有的类或接口添加一个返回类型,每一个扩展或实现must的类也必须改变,否则会出现和上面NotPossible例子中一样的错误。

在PHP 8.0中增加了Union Types,可以指定大多数内部函数和方法的返回类型;但如果为任何没有标记final的类或接口方法指定它,会立即破坏大量的代码。

因此,取而代之的是添加了"暂定"返回类型的概念:正确的返回类型被记录下来,但通常是错误的,而是问题中显示的弃用通知。

#[\ReturnTypeWillChange]的属性

额外的问题是,很多代码需要能够在多个版本的PHP上运行,而一些增加的返回类型在8.0之前的版本中是无效的。所以,为了表示在代码中对返回类型的计划改变,可以添加特殊的属性 #[\ReturnTypeWillChange]。这对于老版本的 PHP 来说看起来像一个注释,但却告诉 PHP 8.1 不要提出弃用通知。然后,一旦你不需要支持旧版本的 PHP,你就可以修复返回类型。

现在应该怎么办?

首先,仔细阅读信息,找出你需要改变的方法,以及正确的返回类型是什么。在上面的例子中。

Return type of Example::count() ...

这说明Example类上的count方法需要改变......。

...应该与Countable::count(): int兼容 ...

......而预期的返回类型是int,如Countable接口上所定义的那样

下一步,决定你能做什么。

  • 需要改变的库或扩展中的代码是别人写的吗?检查是否有新的版本可用。因为这只是一个废弃版本,所以可以忽略它,给作者时间来修复它,
  • 您的class是否已经返回正确的类型?在我们的示例中,是的,42 对于int 的返回类型是有效的。
  • 你能安全地改变该方法的返回类型吗?如果你在自己的应用程序上工作,答案可能是"是",只要你更新extend这个类。如果你正在处理库中的代码,用户可能已经扩展了这个类,你就需要考虑对他们的影响。

如果你决定这很安全,你可以简单地添加返回类型,如通知中所示。

class Example implements Countable {
    public function count(): int {
        return 42;
    }
}

如果你需要支持旧版本的PHP,或者还没有更新代码的用户,你可以暂时抑制该通知。

class Example implements Countable {
    #[\ReturnTypeWillChange]
    public function count() {
        return 42;
    }
}

请务必注意,内部返回类型可能会在 PHP 9.0 中强制执行,因此请确保您有一个可靠的计划来更改使用此属性标记的方法。

IMSoP 提问于2022-02-15
IMSoP 修改于2022-03-21
我想知道为什么我们没有看到Stringable的这种消减?3v4l.org/Plutn#v8.1.2Rain 2022-02-16
@Rain Stringable是一个奇怪的特例,它违背了语言的正常规则,原因我不太明白。然而,如果你为__toString提供了错误的返回类型,不管是否明确提到了接口,你确实会得到一个错误。3v4l.org/fcNqlIMSoP 2022-02-16
如果能有一个支持php5的工作机会就更好了。我希望能看到类似的东西。[#Compat\ReturnType(bool)],这样就不会出现废止的情况了。giftnuss 2022-02-21
@giftnuss #[\ReturnTypeWillChange] 在 PHP 5 下可以正常工作,因为它被看作是一个注释。不过在同一个程序中同时支持PHP 5和PHP 9的机会非常小,所以如果你真的坚持支持PHP 5(它在三年前就有了最后的正式版本),你可能最好忽略所有的弃用,直到你能更新。IMSoP 2022-02-21
@giftnuss PHP5是EOL,没有收到任何安全覆盖或补丁。任何存在的安全漏洞都可能永远不会得到修复,即使它被发现了。拥有向后兼容的代码是好的,但使用PHP5是不安全的,因为它不被支持。mbomb007 2022-06-10
#2楼
得票数 -3

我的问题是通过在函数前添加#[\ReturnTypeWillChange]来解决的。 在此输入图片描述

Aashir Azeem 提问于2022-10-06
这并没有真正说明错误信息本身所没有的内容,而且正如我的答案所解释的,如果你只是升级自己的应用程序而不需要支持多个版本的PHP,这可能不是正确的解决方案。也没有理由对文本进行截图,直接将文本粘贴到答案中即可。IMSoP 2022-10-06
标签