参考:Return type of ... should either be compatible with ..., or the #[\ReturnTypeWillChange] attribute should be used
Reference: Return type of ... should either be compatible with ..., or the #[\ReturnTypeWillChange] attribute should be used
在 PHP 8.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
这是什么意思,我该如何解决?
背景:Return 类型和协方差
从PHP 7.0开始,可以指定函数或方法的return类型,例如function example(): string
到表示 return 是一个字符串的函数。这形成了其他代码可以依赖的契约。
例如,这个 class 承诺 getList
方法将 return 某种 Iterator
:
class Base {
public function getList(): Iterator {
// ...
}
}
可以编写调用代码,知道如果 $foo instanceOf Base
为真,则 $foo->getList() instanceOf Iterator
也将为真。
如果扩展 class,可以指定 相同 return 类型,或 更具体 return 类型(称为“协方差”的规则),并且调用代码的假设仍然为真:
class SubClass extends Base {
public function getList(): DirectoryIterator {
// ...
}
}
$foo = new SubClass;
var_dump($foo instanceOf Base); // true
var_dump($foo->getList() instanceOf Iterator); // true
但是如果您指定不同的 return类型,或者根本没有return类型,假设不成立,所以 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!
向后兼容性和弃用
如果您将 return 类型添加到现有的 class 或接口,每个 class 扩展或实现 也必须 进行更改,否则它会给出与上面 NotPossible
示例中相同的错误。
在PHP8.0中加入Union Types,可以指定大部分内部函数和方法的return类型;但是为任何 class 或未标记 final
的接口方法指定它会立即破坏很多代码。
因此,相反,添加了“暂定”return 类型的概念:记录了正确的 return 类型,但通常会出现错误的是中所示的弃用通知问题。
#[\ReturnTypeWillChange]
属性
额外的问题是很多代码需要能够在 PHP 的多个版本上 运行,并且一些添加的 return 类型在8.0之前的版本。因此,要在代码中指示 planned 更改为 returned 类型,您可以添加特殊的 attribute #[\ReturnTypeWillChange]
。这看起来像是对旧版本 PHP 的评论,但告诉 PHP 8.1 不要提出弃用通知。然后,一旦你不需要支持旧版本的PHP,你可以修复return类型。
你现在应该做什么?
首先,仔细阅读消息,找出您需要更改的方法,正确的return类型是什么。在上面的例子中:
Return type of Example::count() ...
这表示 Example
class 上的 count
方法需要更改 ...
... should either be compatible with Countable::count(): int ...
... 并且预期的 return 类型是 int
,如 Countable
接口
上所定义
接下来,决定你能做什么:
- 库或扩展中需要更改的代码是别人写的吗?检查是否有可用的新版本。 由于这只是一个弃用,可以忽略它并给作者时间来修复它。
- 你的 class 已经 return 是正确的类型了吗?在我们的示例中,是的,
42
对 int
的 return 类型有效。
- 你能安全地改变方法的 return 类型吗?如果您正在开发自己的应用程序,那么答案可能是“是”,只要您更新 classes 那
extend
这个。如果您正在处理用户可能扩展此 class 的库代码,您将需要考虑对他们的影响。
如果您认为它是安全的,您可以简单地添加 return 类型,如通知所示:
class Example implements Countable {
public function count(): int {
return 42;
}
}
如果您需要支持旧版本的PHP,或者尚未更新代码的用户,您可以暂时取消通知:
class Example implements Countable {
#[\ReturnTypeWillChange]
public function count() {
return 42;
}
}
请务必注意,内部 return 类型可能会 在 PHP 9.0 中强制执行,因此请确保您有一个可靠的更改计划使用此属性标记的方法。
在 PHP 8.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
这是什么意思,我该如何解决?
背景:Return 类型和协方差
从PHP 7.0开始,可以指定函数或方法的return类型,例如function example(): string
到表示 return 是一个字符串的函数。这形成了其他代码可以依赖的契约。
例如,这个 class 承诺 getList
方法将 return 某种 Iterator
:
class Base {
public function getList(): Iterator {
// ...
}
}
可以编写调用代码,知道如果 $foo instanceOf Base
为真,则 $foo->getList() instanceOf Iterator
也将为真。
如果扩展 class,可以指定 相同 return 类型,或 更具体 return 类型(称为“协方差”的规则),并且调用代码的假设仍然为真:
class SubClass extends Base {
public function getList(): DirectoryIterator {
// ...
}
}
$foo = new SubClass;
var_dump($foo instanceOf Base); // true
var_dump($foo->getList() instanceOf Iterator); // true
但是如果您指定不同的 return类型,或者根本没有return类型,假设不成立,所以 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!
向后兼容性和弃用
如果您将 return 类型添加到现有的 class 或接口,每个 class 扩展或实现 也必须 进行更改,否则它会给出与上面 NotPossible
示例中相同的错误。
在PHP8.0中加入Union Types,可以指定大部分内部函数和方法的return类型;但是为任何 class 或未标记 final
的接口方法指定它会立即破坏很多代码。
因此,相反,添加了“暂定”return 类型的概念:记录了正确的 return 类型,但通常会出现错误的是中所示的弃用通知问题。
#[\ReturnTypeWillChange]
属性
额外的问题是很多代码需要能够在 PHP 的多个版本上 运行,并且一些添加的 return 类型在8.0之前的版本。因此,要在代码中指示 planned 更改为 returned 类型,您可以添加特殊的 attribute #[\ReturnTypeWillChange]
。这看起来像是对旧版本 PHP 的评论,但告诉 PHP 8.1 不要提出弃用通知。然后,一旦你不需要支持旧版本的PHP,你可以修复return类型。
你现在应该做什么?
首先,仔细阅读消息,找出您需要更改的方法,正确的return类型是什么。在上面的例子中:
Return type of Example::count() ...
这表示 Example
class 上的 count
方法需要更改 ...
... should either be compatible with Countable::count(): int ...
... 并且预期的 return 类型是 int
,如 Countable
接口
接下来,决定你能做什么:
- 库或扩展中需要更改的代码是别人写的吗?检查是否有可用的新版本。 由于这只是一个弃用,可以忽略它并给作者时间来修复它。
- 你的 class 已经 return 是正确的类型了吗?在我们的示例中,是的,
42
对int
的 return 类型有效。 - 你能安全地改变方法的 return 类型吗?如果您正在开发自己的应用程序,那么答案可能是“是”,只要您更新 classes 那
extend
这个。如果您正在处理用户可能扩展此 class 的库代码,您将需要考虑对他们的影响。
如果您认为它是安全的,您可以简单地添加 return 类型,如通知所示:
class Example implements Countable {
public function count(): int {
return 42;
}
}
如果您需要支持旧版本的PHP,或者尚未更新代码的用户,您可以暂时取消通知:
class Example implements Countable {
#[\ReturnTypeWillChange]
public function count() {
return 42;
}
}
请务必注意,内部 return 类型可能会 在 PHP 9.0 中强制执行,因此请确保您有一个可靠的更改计划使用此属性标记的方法。