写点什么

一文看懂 PHP 8 的新特性

  • 2020-07-28
  • 本文字数:6271 字

    阅读完需:约 21 分钟

一文看懂PHP 8的新特性

本文最初发布于 stitcher 博客, 经原作者授权由 InfoQ 中文站翻译并分享。


2020 年 11 月 26 日,PHP 8 将正式发布。这是一个新的主要版本,它将引入一些重大更改,以及许多新特性和性能改进。PHP 8 目前正处于非常活跃的开发阶段,其第一个 Alpha 版已经于 2020 年 6 月 26 日发布。


因为新版引入了许多重大更改,你很有可能需要对代码进行一些更改才能使其运行在 PHP 8 上。如果你一直都在使用最新版本,那么这一次的升级也应该不会很难,因为多数重大更改都已在之前的 7.*版本中弃用了。不用担心,所有这些弃用的内容都在本文中列出来了。


除了重大更改外,PHP 8 还带来了一组不错的新特性,例如JIT编译器联合类型Attributes等。

新特性

首先,我们来看新特性。请记住 PHP 8 仍在积极开发当中,因此这个列表会随着时间的推移而变长。

联合类型

鉴于 PHP 具有动态类型的性质,在很多情况下联合类型是很有用的。联合类型(Union Types)是两种或多种类型的集合,用户可以使用其中一种。


public function foo(Foo|Bar $input): int|float; 
复制代码


请注意,void永远不能成为联合类型的一部分,因为它表示“根本没有返回值”。此外,可以使用|null或使用现有的?符号来写nullable的联合类型:


public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void; 
复制代码

JIT

JIT(即时)编译器可以显著提升性能,不过,它并不总是在 Web 请求的上下文中。目前还没有准确的基准测试可用,但将来肯定会有的。


如果你想进一步了解 JIT 对 PHP 的作用,可以阅读我在这里写的另一篇文章

Attributes

Attributes在其他语言中通常称为 annotations,它提供了一种向类添加元数据的方法,这种方法无需解析文档块。


下面是 RFC 中提供的 Attributes 示例:


use App\Attributes\ExampleAttribute; @@ExampleAttribute class Foo {     @@ExampleAttribute     public const FOO = 'foo';       @@ExampleAttribute     public $x;       @@ExampleAttribute     public function foo(@@ExampleAttribute $bar) { } } 
复制代码


@@Attribute class ExampleAttribute {     public $value;       public function __construct($value)     {         $this->value = $value;     } } 
复制代码


请注意,这个基本的Attribute在之前的 RFC 中曾称为PhpAttribute,但之后在另一个 RFC 中改成现在的样子。如果你想深入了解 attributes 的工作机制,以及如何构建自己的 Attributes,可以阅读这篇深入解析 Attributes 的博客

Match 表达式

你可以称之为switch表达式的老大哥:match可以返回值,不需要break语句,可以组合条件,使用严格的类型比较,并且不执行任何强制类型转换(type coercion) 。


它是这个样子:


$result = match($input) {     0 => "hello",     '1', '2', '3' => "world", }; 
复制代码


要了解 Match 表达式的细节,请看这里

Constructor property promotion

这个 RFC 添加了语法糖来创建值对象或数据传输对象。现在 PHP 不用再为它们指定类属性和一个构造器,可以将它们组合为一个。


以前是这样做:


class Money  {     public Currency $currency;       public int $amount;       public function __construct(         Currency $currency,         int $amount,     ) {         $this->currency = $currency;         $this->amount = $amount;     } } 
复制代码


现在可以这样:


class Money  {     public function __construct(         public Currency $currency,         public int $amount,     ) {} } 
复制代码


关于 property promotion 的更多信息,可以参考这篇专门介绍它的文章

新的 static 返回类型

虽然现在的 PHP 已经可以返回self,但是直到 PHP 8 中static才是有效的返回类型。考虑到 PHP 动态类型的性质,这个特性对许多开发人员都非常有用。


class Foo {     public function test(): static     {         return new static();     } } 
复制代码

新的 mixed 类型

有些人可能称其为必要之恶:mixed类型会让很多人感到困惑。不过,加入它的决定也是有理由的:在 PHP 中,缺少某种类型可能有很多后果:


  • 函数不返回任何内容或返回 null

  • 我们期望的是某种类型

  • 我们期望的类型在 PHP 中无法被类型提示


由于上述原因,增加mixed类型是一件好事。mixed本身是以下类型之一:


  • array

  • bool

  • callable

  • int

  • float

  • null

  • object

  • resource

  • string


注意,mixed也可以用作参数或属性类型,而不仅仅是返回类型。


另外请注意,由于mixed已经包含null,因此不允许将其设置为nullable。以下内容将触发错误:


// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type. function bar(): ?mixed {} 
复制代码

Throw 表达式

该 RFC 将throw从语句变为表达式,这样就可以在许多新场景中抛出异常:


$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset'); 
复制代码

用私有方法继承

以前,PHP 曾经对公共、保护和私有方法应用相同的继承检查。换句话说:私有方法应遵循与保护方法和公共方法相同的方法签名规则。这是没有道理的,因为子类将无法访问私有方法。


该 RFC 更改了这个行为,因此不再对私有方法执行这些继承检查。此外,使用final private function也没有意义,因此,现在它将触发警告:


Warning: Private methods cannot be final as they are never overridden by other classes 
复制代码

Weak maps

在 PHP 7.4 中添加的weakrefs RFC的基础上,PHP 8 添加了WeakMap实现。WeakMap保存对对象的引用,这不会阻止这些对象被垃圾回收。


以 ORM 为例,它们通常会实现缓存,其缓存保存对实体类的引用,以提高实体之间关系的性能。只要该缓存具有对这些实体对象的引用,就不能对其进行垃圾回收,即使该缓存是唯一引用它们的对象也是如此。


如果该缓存层使用了弱引用和映射,则 PHP 将在没有其他引用时对这些对象进行垃圾回收。尤其是对于 ORM,它可以管理一个请求中的数百个(乃至数千个)实体。Weak maps(弱映射)可以提供一种更好,对资源更友好的方式来处理这些对象。


这是 RFC 中 Weak maps 的示例:


class Foo  {     private WeakMap $cache;       public function getSomethingWithCaching(object $obj): object     {         return $this->cache[$obj]            ??= $this->computeSomethingExpensive($obj);     } } 
复制代码

在对象上允许::class

一个小的但有用的新特性:现在可以在对象上使用::class,而不必使用get_class()。它的工作方式与get_class()相同。


$foo = new Foo(); var_dump($foo::class); 
复制代码

非捕获 catches

在 PHP 8 之前,每当你想捕获一个异常时都必须将其存储在一个变量中,不管你是否使用这个变量。现在使用非捕获 catches,你也可以忽略变量。以前是这样:


try {     // Something goes wrong } catch (MySpecialException $exception) {     Log::error("Something went wrong"); } 
复制代码


现在可以执行以下操作:


try {     // Something goes wrong } catch (MySpecialException) {     Log::error("Something went wrong"); } 
复制代码


请注意,你必须始终指定类型,不允许使用空catch。如果要捕获所有的异常和错误,可以使用Throwable作为捕获类型。

参数列表中的尾部逗号

现在的 PHP,虽然可以调用函数时在尾部加逗号,但参数列表中仍然缺少对尾部逗号的支持。PHP 8 现在允许使用它,也就是说你可以执行以下操作:


public function(     string $parameterA,     int $parameterB,     Foo $objectfoo, ) {     // 注意上面最后一个逗号… } 
复制代码

从接口创建DateTime对象

你已经可以使用DateTime::createFromImmutable($immutableDateTime)DateTimeImmutable对象创建DateTime对象,但反过来就很麻烦。


PHP 8 添加了DateTime::createFromInterface()DatetimeImmutable::createFromInterface(),所以现在有一种通用的方法可以将DateTimeDateTimeImmutable对象彼此转换。


DateTime::createFromInterface(DateTimeInterface $other); DateTimeImmutable::createFromInterface(DateTimeInterface $other); 
复制代码

新的Stringable接口

Stringable接口可用于类型提示任何字符串或实现__toString()的内容。此外,每当一个类实现__toString()时,它就会自动实现幕后接口,而无需手动实现。


class Foo {     public function __toString(): string     {         return 'foo';     } } function bar(Stringable $stringable) { /* … */ } bar(new Foo()); bar('abc'); 
复制代码

新的str_contains()函数

有人可能会说它早就该来了,总之我们终于不必再依赖strpos来知道一个字符串是否包含另一个字符串了。


以前是这样做:


if (strpos('string with lots of words', 'words') !== false) { /* … */ } 
复制代码


现在,你可以这样:


if (str_contains('string with lots of words', 'words')) { /* … */ } 
复制代码

新的str_starts_with()str_ends_with()函数

另外两个早就该做的函数,现在已加入核心。


str_starts_with('haystack', 'hay'); // true str_ends_with('haystack', 'stack'); // true 
复制代码

新的fdiv()函数

新的fdiv()函数与fmod()intdiv()函数的功能相似,允许被 0 除。根据情况你会得到INF-INFNAN,而不是错误。

新的get_debug_type()函数

get_debug_type()返回一个变量的类型。听起来像gettype()的功能?get_debug_type()为数组、字符串、匿名类和对象返回更有用的输出。


例如,在类\Foo\Bar上调用gettype()将返回object。使用get_debug_type()将返回类名称。


可以在 RFC 中找到get_debug_type()gettype()之间差异的完整列表。

新的get_resource_id()函数

Resources 是 PHP 中的特殊变量,指的是外部资源。一个例子是 MySQL 连接,另一个是文件句柄。


这些资源中每一个都分配了一个 ID,但以前唯一知道该 ID 的方法是将资源转换为int


$resourceId = (int) $resource; 
复制代码


PHP 8 添加了get_resource_id()函数,让这个操作更加明显易懂,且类型安全:


$resourceId = get_resource_id($resource); 
复制代码

raits 改进中的抽象方法

Traits 可以指定抽象方法,这些方法必须由使用它们的类实现。需要注意的是:在 PHP 8 之前,这些方法实现的签名没有被验证。以下写法是有效的:


trait Test {     abstract public function test(int $input): int; } class UsesTrait {     use Test;     public function test($input)     {         return $input;     } } 
复制代码


在 PHP 8 中,当使用一个 trait 并实现其抽象方法时,PHP 8 将执行正确的方法签名验证。这意味着你需要编写以下代码:


class UsesTrait {     use Test;     public function test(int $input): int     {         return $input;     } } 
复制代码

token_get_all()的对象实现

token_get_all()函数返回一个值数组。该 RFC 使用PhpToken::getAll()方法添加了PhpToken类。此实现适用于对象而不是普通值。它消耗的内存更少,并且更容易阅读理解。

可变语法调整

根据 RFC:“统一变量语法 RFC 解决了 PHP 变量语法中的许多不一致之处。而本 RFC 旨在解决一小部分被忽略的情况。”

内部函数的类型注解

许多人开始为所有内部函数添加适当的类型注释。这个问题历史很久了,而 PHP 之前版本所做的一系列更改终于为解决它铺平了道路。这意味着内部函数和方法将反映出完整的类型信息。

ext-json始终可用

以前,可以在不启用 JSON 扩展的情况下编译 PHP,以后就不行了。现在,开发人员知道 JSON 是一直能用的,而不需要提前确认扩展是否可用。由于 JSON 非常流行,所以这个改进很方便。

重大更改

如前所述:PHP 8 是一个重大更新,因此会有很多重大更改。最好在UPGRADING文档中查看重大更改的完整列表。


但许多重大更改在以前的 7.*版本中已经弃用,因此如果你多年来一直紧跟新版,那么升级到 PHP 8 并不会有什么困难。

一致的类型错误

现在 PHP 的用户定义函数会抛出TypeError,但内部函数并不会,而是发出警告并返回null。从 PHP 8 开始,内部函数的行为也是一样了。

重新分类的引擎警告

以前,许多仅触发警告或通知的错误已转换为合适的错误类型。以下警告已更改。


  • Undefined 变量:Error异常取代了通知

  • Undefined 数组索引:警告取代了通知

  • 除以零:DivisionByZeroError异常取代了警告

  • 尝试增加/减少非对象的’%s’属性:Error异常取代了警告

  • 尝试修改非对象的’%s’属性:Error异常取代了警告

  • 尝试分配非对象的’%s’属性:Error异常取代了警告

  • 从空值创建默认对象:Error异常取代了警告

  • 试图获取非对象的’%s’属性:警告取代了通知

  • 未定义的属性:%s::$%s:警告取代了通知

  • 由于下一个元素已被占用,无法将元素添加到数组:Error异常取代了警告

  • 无法取消设置非数组变量中的偏移量:Error异常取代了警告

  • 无法将标量值用作数组:Error异常取代了警告

  • 只能解包数组和TraversablesTypeError异常取代了警告

  • 为 foreach()提供了无效参数:TypeError异常取代了警告

  • 偏移量类型非法:TypeError异常取代了警告

  • isset 中的偏移量类型非法或为空:TypeError异常取代了警告

  • unset 中的偏移量类型非法:TypeError异常取代了警告

  • 数组到字符串的转换:警告取代了通知

  • Resource ID#%d 用作偏移量,转换为整数(%d):警告取代了通知

  • 发生字符串偏移量转换:警告取代了通知

  • 未初始化的字符串偏移量:%d:警告取代了通知

  • 无法将空字符串分配给字符串偏移量:Error异常取代了警告

  • 提供的资源不是有效的流资源:TypeError异常取代了警告

@运算符不再让致命错误静默

此更改可能会揭示出 PHP 8 之前隐藏的错误。请确保在生产服务器上设置display_errors=Off

默认错误报告级别

现在是E_ALL,而不是E_NOTICEE_DEPRECATED。这意味着新版可能会弹出许多错误,这些错误在 PHP 8 以前会被静默忽略。

默认 PDO 错误模式

根据 RFC:当前,PDO 的默认错误模式为静默。换句话说,当发生 SQL 错误时,除非开发人员实现自己的显式错误处理,否则不会发出错误或警告,也不会引发异常。


此 RFC 更改后,默认错误将更改为PDO::ERRMODE_EXCEPTION

串联优先级

这一更改在 PHP 7.4 中已弃用,现在正式移除。如果你要编写这样的内容:


echo "sum: " . $a + $b; 
复制代码


PHP 以前会这样解释它:


echo ("sum: " . $a) + $b; 
复制代码


PHP 8 将改为这种解释:


echo "sum: " . ($a + $b); 
复制代码

对算术和按位运算符进行更严格的类型检查

在 PHP 8 之前,可以在数组、资源或对象上应用算术或按位运算符。现在就不行了,新版将抛出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 和 PHP 8,则允许以下签名:


ReflectionClass::newInstance($arg = null, ...$args); ReflectionFunction::invoke($arg = null, ...$args); ReflectionMethod::invoke($object, $arg = null, ...$args); 
复制代码

稳定的排序

在 PHP 8 之前,排序算法是不稳定的。这意味着不能保证相等元素的顺序。PHP 8 将所有排序函数的行为更改为稳定排序。

不兼容方法签名的致命错误

根据 RFC:由于不兼容的方法签名而导致的继承错误现在会引发致命错误或警告,具体取决于错误原因和继承层次结构。

其他弃用和更改

在 PHP7.*开发过程中加入了几个弃用,这些弃用现已在 PHP 8 中正式移除。



英文原文 :


https://stitcher.io/blog/new-in-php-8


2020-07-28 09:587371
用户头像
王强 技术是文明进步的力量

发布了 836 篇内容, 共 443.8 次阅读, 收获喜欢 1753 次。

关注

评论

发布
暂无评论
发现更多内容

数据API开发如何快速上手:先了解什么是数据API生命周期管理

雨果

API 数据api

模块一作业

Geek_1264yp

一比一手写迷你版vue,彻底搞懂vue运行机制

hellocoder2029

JavaScript

SAE 助力贵州酒店集团从容支撑贵州特产抢购

阿里巴巴中间件

阿里云 Serverless 云原生 SAE

想从事运维岗位应该学习什么技能?谁能告诉一下?

行云管家

运维 网络运维 IT运维

公司用的堡垒机叫什么?多少钱?

行云管家

网络安全 堡垒机 等级保护 过等保

字节半天*3面/5天拿offer,全凭自身硬实力和这份Java面试笔记

钟奕礼

Java 面试 java;

Java | this和super关键字【深入理解子类和父类的继承关系】

Fire_Shield

super this 9月月更

开发者有话说|成长之路

六月的雨在InfoQ

个人成长 开会 996 007 9月月更

50道Java集合高频面试题,看完面试成功率99%

钟奕礼

Java 面试 java;

idea 远程开发 client

黄敏

架构实战训练营模块1作业--开启架构之旅

阿姆斯壮

架构实战营 #架构实战营

2022届秋招Java岗高频面试题盘点,老司机也未必全会,真的太卷了

钟奕礼

Java 面试 java;

为什么大数据工程师比数据科学家的需求更大

雨果

数据工程师

LED显示屏价格与品质哪个更重要

Dylan

LED LED显示屏 led显示屏厂家

HTTP - TLS1.3 初次解读

懒时小窝

中心化决议管理——云端分析

字节跳动终端技术

ios 研发效能 CocoaPods 制品库 云化服务

概述数据交换的构建策略

穿过生命散发芬芳

数据交换 9月月更

【Java深入学习】并发常见方法的注意事项

钟奕礼

Java 面试 java;

深入剖析nodejs中间件

coder2028

node.js

Java开发5年,复习1个月成功上岸京东物流,面试和复习思路分享

钟奕礼

Java 面试 java;

2022第三届云原生编程挑战赛--Serverless VSCode WebIDE使用体验

六月的雨在InfoQ

Serverless 边缘容器 9月月更 Serverless VSCode WebIDE 线上ide

EMQ荣获工信部第五届“绽放杯”5G应用征集大赛智慧金融专题一等奖

EMQ映云科技

5G 物联网 IoT 数智化 9月月更

总览 Java 容器--集合框架的体系结构

钟奕礼

Java 面试 java;

玩转 Flowable 流程实例

江南一点雨

Java springboot workflow flowable

java基础面试题

钟奕礼

编程 java;

IP地址和MAC地址都可以确定目标地址,为什么二者都在使用,舍弃一个是否可行?

阿柠xn

Mac IP 网络 协议族 9月月更

手写vue-router核心原理

hellocoder2029

Vue

时隔一年多 jQuery 再度发布 3.6.1 新版本,你还在用JQ吗?

茶无味的一天

JavaScript 前端 框架 ​jQuery

组装式交付-云巧 知多少

六月的雨在InfoQ

9月月更 云巧 组装式交付 云巧资产 云巧工坊

阿里云服务器ECS基本操作指南

六月的雨在InfoQ

阿里云 SSH xshell 云服务器ECS 9月月更

一文看懂PHP 8的新特性_编程语言_Brent_InfoQ精选文章