Reference: Return type of ... should either be compatible with ..., or the #[\ReturnTypeWillChange] attribute should be used
在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
这是什么意思,我应该如何解决这个问题?
背景:返回类型和协方差
自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 中强制执行,因此请确保您有一个可靠的计划来更改使用此属性标记的方法。
Stringable
是一个奇怪的特例,它违背了语言的正常规则,原因我不太明白。然而,如果你为__toString
提供了错误的返回类型,不管是否明确提到了接口,你确实会得到一个错误。3v4l.org/fcNql
- IMSoP 2022-02-16
#[\ReturnTypeWillChange]
在 PHP 5 下可以正常工作,因为它被看作是一个注释。不过在同一个程序中同时支持PHP 5和PHP 9的机会非常小,所以如果你真的坚持支持PHP 5(它在三年前就有了最后的正式版本),你可能最好忽略所有的弃用,直到你能更新。
- IMSoP 2022-02-21