参考: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 是正确的类型了吗?在我们的示例中,是的,42int 的 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 中强制执行,因此请确保您有一个可靠的更改计划使用此属性标记的方法。