罗列出 PHP8 中会发生的一些改变:新功能、性能改进和突破性变化。
新特性
联合类型(Union types)
考虑到 PHP 的动态类型特性,联合类型在很多情况下都很有用。
联合类型是两个或多个类型的集合,这些类型指示可以使用这两个类型中的任何一个。
1 | public function foo(Foo|Bar $input): int|float; |
请注意,void 永远不能是联合类型的一部分,因为它表示 “根本没有返回值”。
此外,可以使用 |NULL
或使用现有的 ?
。
1 | public function foo(Foo|null $foo): void; |
JIT
JIT-Just-In-Time 编译器承诺显著提高性能,尽管在 Web 应用可能没有较大的好处。
静态返回类型 (Static return types)
虽然已经可以返回 self ,但在 PHP8 之前,静态不是有效的返回类型。考虑到 PHP 的动态类型特性,它对许多开发人员都很有用。
1 |
|
弱映射
基于在 PHP 7.4 中添加的 WeakRefs
RFC 的基础上,在 PHP 8 中 添加了 WeakMap 实现。WeakMap
包含对对象的引用,这不会阻止这些对象被垃圾回收。
以 ORM 为例,它们经常实现包含对实体类的引用的缓存,以提高实体之间关系的性能。
这些实体对象不能被垃圾回收,只要该缓存有对它们的引用,即使缓存是唯一引用它们的东西。
如果该缓存层改为使用弱引用和映射,则 PHP 将在其他对象不再引用这些对象时对它们进行垃圾回收。
特别是在 ORM 的情况下,它可以在一个请求中管理数百个 (如果不是数千个) 实体;弱映射可以提供一种更好、更资源友好的方式来处理这些对象。
以下是 Weak maps 的用法,RFC 中的一个示例:
1 |
|
可以在对象上使用 ::class
一个小而有用的新特性:现在可以对对象使用 ::class
,而不必对它们使用 get_class()
。
它的工作方式与 get_class()
相同。
1 | $foo = new Foo(); |
创建 DateTime 对象的接口
您已经可以使用 DateTime::createFromImmutable($immutableDateTime)
,从 DateTimeImmutable
对象创建 DateTime
对象,但是反过来很棘手。
通过添加 DateTime::createFromInterface()
和 DatetimeImmutable::createFromInterface()
,现在有了一种将 DateTime
和 DateTimeImmutable
对象相互转换的通用方法。
1 | DateTime::createFromInterface(DateTimeInterface $other); |
新的 Stringable
接口
Stringable
接口可用于键入提示任何字符串或实现__toString()
。
此外,每当类实现 __toString()
时,它都会自动在幕后实现接口,不需要手动实现它。
1 | class Foo |
新的 str_contains()
函数
有些人可能会说这是早就应该实现的功能,但是我们最终不必再依赖
strpos()
来知道一个字符串是否包含另一个字符串。
- 以前:
1 | if (strpos('string with lots of words', 'words') !== false) { /* … */ } |
- 现在:
1 | if (str_contains('string with lots of words', 'words')) { /* … */ } |
新的 fdiv()
函数
新的 fdiv()
函数的作用类似于 fmod()
和 intdiv()
函数,它们允许被 0 整除。
您将得到 INF
、-INF
或 NaN
,而不是错误,具体取决于大小写。
新的 get_debug_type()
函数
get_debug_type()
返回一个变量的类型。
听起来像是 gettype()
可以实现的功能。get_debug_type()
为数组、字符串、匿名类和对象返回更有用的输出。
例如,在类 \foo\Bar
上调用 gettype()
将返回 Object
。
使用 get_debug_type()
将返回类名。
改进 traits
里的抽象方法
traits 可以指定必须由使用它们的类实现的抽象方法。
但是有一个警告:在 PHP8 之前,这些方法实现的签名没有经过验证。
在以下代码中有效:
1 | trait Test { |
在使用 traits 并实现其抽象方法时,PHP8 将执行正确的方法签名验证。
这意味着您需要改写以下内容:
1 | class UsesTrait |
token_get_all()
的对象接口 RFC
函数的作用是:返回值的是一个数组。
此 RFC 使用 PhpToken::getall()
方法添加一个 PhpToken
类。
此实现使用对象,而不是普通值。
它消耗更少的内存,更容易阅读。
统一错误类型 RFC
PHP 中的用户定义函数已经抛出 TypeErrors
,但是内部函数没有抛出 TypeErrors
,而是发出警告并返回 NULL
。
从 PHP8 开始,内部函数的行为已经保持一致。
默认错误报告级别
现在是 E_ALL
,而不是除 E_NOTICE
和 E_DEVERATED
之外的所有内容。
这意味着可能会弹出许多以前被悄悄忽略的错误,尽管在 PHP8 之前可能已经存在
@运算符不再忽略致命错误
此更改可能会揭示在 PHP8 之前隐藏的错误。请确保在生产服务器上设置 display_errors=off
串联优先级 RFC
虽然在 PHP7.4 中已不推荐使用,但此更改现在生效。
如果你这样写的话:
1 | echo "sum: " . $a + $b; |
PHP 以前会这样解释它:
1 | echo ("sum: " . $a) + $b; |
PHP 8 将会这样解释它:
1 | echo "sum: " . ($a + $b); |
反射方法签名更改
反射类的三个方法签名已更改:
1 | ReflectionClass::newInstance($args); |
现已成为:
1 | ReflectionClass::newInstance(...$args); |
升级指南指定,如果您扩展了这些类,并且仍然希望同时支持 PHP 7 和 PHP 8,则允许以下签名:
1 | ReflectionClass::newInstance($arg = null, ...$args); |