参考:...的返回类型应该与...兼容,或者应该使用#[\ReturnTypeWillChange]属性

IMS*_*SoP 39 php php-8.1

在 PHP 8.1 中,以下代码在以前的版本中有效:

class Example implements Countable {
    public function count() {
        return 42;
    }
}
Run Code Online (Sandbox Code Playgroud)

提出弃用通知:

已弃用:Example::count() 的返回类型应与 Countable::count(): int 兼容,或者应使用 #[\ReturnTypeWillChange] 属性暂时抑制通知

这是什么意思,我应该如何解决它?

IMS*_*SoP 63

背景:返回类型和协方差

从 PHP 7.0 开始,可以指定函数或方法的返回类型,例如function example(): string指示返回字符串的函数。这形成了其他代码可以依赖的契约。

例如,此类承诺该getList方法将返回某种类型Iterator

class Base {
    public function getList(): Iterator {
       // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

编写调用代码时可以知道如果$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
Run Code Online (Sandbox Code Playgroud)

但是,如果您指定不同的返回类型,或者根本没有返回类型,则假设将被破坏,因此 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!
Run Code Online (Sandbox Code Playgroud)

向后兼容性和弃用

如果将返回类型添加到现有的类或接口,则每个扩展或实现的类也必须更改,否则将给出与NotPossible上例相同的错误。

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

因此,相反,添加了“暂定”返回类型的概念:记录了正确的返回类型,但通常是错误的是问题中显示的弃用通知。

属性#[\ReturnTypeWillChange]

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

你现在应该做什么?

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

Example::count() 的返回类型 ...

这表示count该类的方法Example需要更改......

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

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

接下来,决定你可以做什么:

  • 库或扩展中需要更改的代码是其他人编写的吗?检查是否有新版本可用。由于这只是一个弃用,因此可以忽略它并给作者时间来修复它。
  • 您的类是否已返回正确的类型?在我们的示例中,yes42对于返回类型有效int
  • 您可以安全地更改方法的返回类型吗?如果您正在开发自己的应用程序,答案可能是“是”,只要您更新这个类extend。如果您正在处理用户可能扩展此类的库代码,您将需要考虑对他们的影响。

如果您认为它是安全的,您可以简单地添加返回类型,如通知中所示:

class Base {
    public function getList(): Iterator {
       // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您需要支持旧版本的 PHP,或者尚未更新代码的用户,您可以暂时隐藏该通知:

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

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

  • @giftnuss `#[\ReturnTypeWillChange]` 在 PHP 5 下可以正常“工作”,因为它被视为注释。不过,在同一个应用程序中同时支持 PHP 5 和 PHP 9 的机会相当渺茫,因此,如果您确实坚持支持 PHP 5([3 年前已发布最终正式版本](https://www.php.com)。 php.net/eol.php)),您可能最好忽略所有弃用,直到您可以实现现代化。 (2认同)
  • @giftnuss PHP5 已停产,没有收到任何安全覆盖或补丁。任何存在的安全漏洞即使被发现,也可能永远不会得到修复。拥有向后兼容的代码很好,但不再安全地使用 PHP5,因为它不受支持。 (2认同)