PHP 8有什么新功能?
新的主要PHP版本PHP8预计将于2020年12月3日发布。这意味着将没有PHP7.5版本。目前,PHP8处于非常活跃的开发中,因此在接下来的几个月中,情况可能会发生很大变化。
在这篇文章中,我将保留最新的预期清单:新功能,性能改进和重大更改。由于PHP8是新的主要版本,因此您的代码被破坏的可能性更高。如果您始终保持最新,那么升级应该不会太难,因为在7.*版本中,大多数重大更改都已被弃用。
除了突破性的变化,PHP8还带来了一些不错的新功能,例如JIT编译器,联合类型,属性和more.AdobeCreativeCloudforTeams,每套起价为$33.99(month.ads通过Carbon)
从新功能开始,请记住PHP8仍在积极开发中,因此此列表会随着时间的推移而增长。
鉴于PHP的动态类型性质,在很多情况下联合类型可能会有用。联合类型是两种或多种类型的集合,表示可以使用这些类型中的任何一种。
public function foo(Foo|Bar $input): int|float;
请注意, void 它永远不能成为联合类型的一部分,因为它表示“根本没有返回值”。此外, nullable 可以使用|null或使用现有的 ? 符号来编写并集 :
public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void;
JIT(尽管及时)编译器有望显着提高性能,尽管并不总是在Web请求的上下文中。目前还没有完成任何准确的基准测试,但是肯定会到来。
如果您想进一步了解JIT对PHP的作用,您可以阅读我在这里写过的另一篇文章。
属性 在其他语言中通常称为 注释,它提供了一种向类添加元数据的方法,而无需解析文档块。
快速浏览一下,这是RFC中的属性外观示例:
use App\Attributes\ExampleAttribute; <> class Foo { < > public const FOO = 'foo'; < > public $x; < > public function foo(< > $bar) { } }
<> class ExampleAttribute { public $value; public function __construct($value) { $this->value = $value; } }
如果您想深入了解属性如何工作以及如何建立自己的属性,您可以在此博客上深入了解属性。
虽然可以返回 self, static 但直到PHP8才是有效的返回类型。考虑到PHP具有动态类型化的性质,此功能对许多开发人员都非常有用。
class Foo { public function test(): static { return new static(); } }
有些人可能会招来必要的恶魔:这种 mixed 类型使许多人产生混杂的感觉。不过,有一个很好的论据:缺少类型可能意味着在PHP中会有很多事情:
函数不返回任何内容或返回null
我们期待着几种类型之一
我们期待一个无法在PHP中提示类型的类型
由于上述原因,mixed 添加类型是一件好事 。 mixed 本身意味着以下类型之一:
array
bool
callable
int
float
null
object
resource
string
注意, mixed 它不仅可以用作返回类型,还可以用作参数或属性类型。
另请注意,由于 mixed 已经包含 null,因此不允许将其设置为可为空。以下内容将触发错误:
//致命错误:混合类型不能为空,null已经是混合类型的一部分。 function bar(): ?mixed {}
该RFC throw 从陈述式变为表达式,这使得在许多新地方引发异常成为可能:
$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
在PHP7.4中添加的weakrefsRFC的基础上,WeakMapPHP8中添加了一个 实现。 WeakMaps 保留对对象的引用,这不会阻止对这些对象进行垃圾收集。
以ORM为例,它们通常实现缓存,该缓存保存对实体类的引用,以提高实体之间关系的性能。只要该高速缓存具有对它们的引用,就不能对其进行垃圾回收,即使该高速缓存是 唯一引用它们的对象也是如此。
如果此缓存层使用了弱引用和映射,则PHP将在没有其他任何引用引用时对这些对象进行垃圾收集。尤其是在ORM的情况下,它可以管理一个请求中的数百个(如果不是数千个)实体。弱映射可以提供一种更好,更资源友好的方式来处理这些对象。
这是RFC的示例,它是弱映射的样子:
class Foo { private WeakMap $cache; public function getSomethingWithCaching(object $obj): object { return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj); } }
一个小的但有用的新功能:现在可以::class 在对象上使用 ,而不必在对象上使用 get_class() 。它的工作方式与相同 get_class()。
$foo = new Foo(); var_dump($foo::class);
每当您想在PHP8之前捕获异常时,无论是否使用该变量,都必须将其存储在变量中。使用非捕获的捕获,您可以忽略变量,所以代替这个:
try { //出问题了 } catch (MySpecialException $exception) { Log::error("Something went wrong"); }
您现在可以执行以下操作:
try { //出问题了 } catch (MySpecialException) { Log::error("Something went wrong"); }
注意,必须始终指定类型,不允许为空 catch。如果要捕获所有异常和错误,可以将其 Throwable 用作捕获类型。
调用函数时已经有可能,但参数列表中仍然缺少尾部逗号支持。PHP8现在允许使用它,这意味着您可以执行以下操作:
public function( string $parameterA, int $parameterB, Foo $objectfoo, ) { //… }
您已经可以使用来DateTime 从DateTimeImmutable对象 创建 对象 DateTime::createFromImmutable($immutableDateTime),但是另一种方法很棘手。通过添加 和 ,现在有一种通用的方式可以将 和 彼此转换 。DateTime::createFromInterface()DatetimeImmutable::createFromInterface()DateTimeDateTimeImmutable
DateTime::createFromInterface(DateTimeInterface $other); DateTimeImmutable::createFromInterface(DateTimeInterface $other);
该 Stringable 接口可用于键入暗示任何字符串或实现的提示 __toString()。而且,每当一个类实现时 __toString(),它都会自动实现幕后的接口,而无需手动实现。
class Foo { public function __toString(): string { return 'foo'; } } function bar(Stringable $stringable) { /* … */ } bar(new Foo()); bar('abc');
有人可能会说它早就该到期了,但是我们终于不必再依赖于 strpos 一个字符串是否包含另一个字符串了。
而不是这样做:
if (strpos('string with lots of words', 'words') !== false) { /* … */ }
您现在可以执行此操作
if (str_contains('string with lots of words', 'words')) { /* … */ }
str_ends_with()
-functions-rfc">新 str_starts_with() 和 str_ends_with() 功能 RFC早该逾期的另外两个,现在已在核心中添加了这两个功能。
str_starts_with('haystack', 'hay'); //真的 str_ends_with('haystack', 'stack'); //真的
新 fdiv() 功能确实为类似的事情 fmod() 和 intdiv() 功能,它允许除以0相反的错误,你会得到的 INF, -INF 或者 NAN,视情况而定。
get_debug_type() 返回变量的类型。听起来像 gettype() 会做什么? get_debug_type() 返回数组,字符串,匿名类和对象的更有用的输出。
例如,调用 gettype() 一个类 \Foo\Bar 将返回 object。使用 get_debug_type() 将返回类名称。
get_debug_type() 和 之间的差异的完整列表 gettype() 可以在RFC中找到。
资源是PHP中的特殊变量,指的是外部资源。一个示例是MySQL连接,另一个示例是文件句柄。
这些资源中的每一个都分配有一个ID,尽管以前唯一知道该ID的方法是将资源转换为 int:
$resourceId = (int) $resource;
PHP8添加了 get_resource_id() 功能,使此操作更加明显且类型安全:
$resourceId = get_resource_id($resource);
特性可以指定抽象方法,这些方法必须由使用它们的类来实现。需要注意的是:在PHP8之前,尚未验证这些方法实现的签名。以下是有效的:
trait Test { abstract public function test(int $input): int; } class UsesTrait { use Test; public function test($input) { return $input; } }
当使用特征并实现其抽象方法时,PHP8将执行正确的方法签名验证。这意味着您需要编写以下代码:
class UsesTrait { use Test; public function test(int $input): int { return $input; } }
该 token_get_all() 函数返回一个值数组。该RFC添加了PhpToken 带有 方法的 类 。此实现适用于对象而不是普通值。它消耗更少的内存,并且更易于阅读。PhpToken::getAll()
RFC:统一变量语法RFC解决了PHP变量语法中的许多不一致之处。该RFC旨在解决一小部分被忽略的案例。”
许多人开始为所有内部函数添加适当的类型注释。这是一个长期存在的问题,最终可以解决以前版本中对PHP所做的所有更改。这意味着内部函数和方法将反映出完整的类型信息。
如前所述:这是一个重大更新,因此会有重大更改。最好的办法是在UPGRADING文档中查看突破性更改的完整列表。
不过,其中许多重大更改在以前的7.*版本中已经过时,因此,如果您多年来一直保持最新,那么升级到PHP8并不难。
PHP中的用户定义函数将已经抛出TypeErrors,但是内部函数没有抛出 ,而是发出警告并返回 null。从PHP8开始,内部函数的行为已变得一致。
以前仅触发警告或通知的许多错误已转换为适当的错误。以下警告已更改。
未定义的变量: Error 异常而不是通知
未定义的数组索引:警告而不是通知
除以零: DivisionByZeroError 异常而不是警告
尝试增加/减少非对象的属性'%s': Error 异常而不是警告
尝试修改非对象的属性'%s': Error异常而不是警告
尝试分配非对象的属性'%s': Error 异常而不是警告
从空值创建默认对象: Error 异常而不是警告
试图获取非对象的属性“%s”:警告而不是通知
未定义的属性:%s::$%s:警告而不是通知
无法将元素添加到数组,因为下一个元素已被占用: Error 异常而不是警告
无法取消设置非数组变量中的偏移量: Error 异常而不是警告
无法将标量值用作数组: Error 异常而不是警告
只能数组,并且 Traversables 可以解压缩: TypeError 异常而不是警告
为foreach():提供了无效的参数: TypeError 异常而非警告
非法的偏移量类型: TypeError 异常而不是警告
isset中的偏移类型非法或为空: TypeError 异常而不是警告
unset中的偏移类型非法: TypeError 异常而不是警告
数组到字符串的转换:警告而不是通知
资源ID#%d用作偏移量,转换为整数(%d):警告而不是通知
发生字符串偏移量强制转换:警告而不是通知
未初始化的字符串偏移量:%d:警告而不是通知
无法将空字符串分配给字符串偏移量: Error异常而不是警告
提供的资源不是有效的流资源: TypeError异常而不是警告
此更改可能会揭示在PHP8之前又被隐藏的错误。请确保display_errors=Off 在生产服务器上进行设置 !
现在 E_ALL 除了E_NOTICE and之外 ,什么都没有 E_DEPRECATED。这意味着可能会弹出许多错误,这些错误以前曾被静默忽略,尽管在PHP8之前可能已经存在。
从RFC: 当前PDO的默认错误模式为静默。这意味着,当发生SQL错误时,除非开发人员实施自己的显式错误处理,否则不会发出任何错误或警告,也不会引发任何异常。
此RFC更改默认错误将PDO::ERRMODE_EXCEPTION 在PHP8中更改为 。
尽管PHP7.4中已弃用,但此更改现已生效。如果您要编写这样的内容:
echo "sum: " . $a + $b;
PHP以前会这样解释它:
echo ("sum: " . $a) + $b;
PHP8将使其如此解释:
echo "sum: " . ($a + $b);
在PHP8之前,可以在数组,资源或对象上应用算术或按位运算符。这不可能了,将抛出一个 TypeError:
[] % [42]; $object + 4;
反射类的三个方法签名已更改:
ReflectionClass::newInstance($args); ReflectionFunction::invoke($args); ReflectionMethod::invoke($object, $args);
现在变成:
ReflectionClass::newInstance(...$args); ReflectionFunction::invoke(...$args); ReflectionMethod::invoke($object, ...$args);
升级指南指定,如果您扩展这些类,并且仍要同时支持PHP7和PHP8,则允许以下签名:
ReflectionClass::newInstance($arg = null, ...$args); ReflectionFunction::invoke($arg = null, ...$args); ReflectionMethod::invoke($object, $arg = null, ...$args);
在PHP7.*开发过程中,添加了几个弃用项,这些弃用项现在已在PHP8中完成。