NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

一文看懂 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:586693
用户头像
王强 技术是文明进步的力量

发布了 788 篇内容, 共 378.6 次阅读, 收获喜欢 1719 次。

关注

评论

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

京东云开发者|mysql基于binlake同步ES积压解决方案

京东科技开发者

MySQL ES 数据同步 MySQL 数据库

Baklib|搭建帮助中心,推动SaaS企业发展

Baklib

SaaS 帮助中心

低码平台标准列表页落地实践,同事直呼好活

Java全栈架构师

Java 程序员 程序人生 低代码开发 低代码平台

Centos7下Docker的安装

我是一个茶壶

容器 ,docker 11月月更

AI技术在基于风险测试模式转型中的应用

百度Geek说

人工智能 AI技术 企业号十月 PK 榜 智能测试

最近面试经常被问到的js手写题

helloworld1024fd

JavaScript

走进 Orca 架构及技术世界

KaiwuDB

数据库·

重磅!涛思数据发布TDengine PI连接器

TDengine

数据库 tdengine 时序数据库

聊聊FinOps

Jianmu

澜舟科技AIGC再进一步,推出澜舟论文助写 LPA,用 AI 帮助写好英文论文

澜舟孟子开源社区

人工智能 nlp 文本生成

写个JS深拷贝,面试备用

helloworld1024fd

JavaScript

假如面试官要你手写一个promise

helloworld1024fd

JavaScript

京东云开发者|代码评审的价值和规范

京东科技开发者

单元测试 代码设计 代码评审 `后端

从华泰证券年报看数字化转型的平台化趋势

三少爷的见

数字化转型 数字化 华泰证券 平台化

共筑使能千行百业的数字底座 | HDC 2022松湖对话顺利召开

OpenHarmony开发者

OpenHarmony

阿里云Imagine Computing创新技术大赛正式开启!

阿里云视频云

阿里云 技术大赛

Baklib|如何才能做好企业内部知识管理?

Baklib

知识管理

链上互助公排代币模式dapp系统开发合约定制

开发微hkkf5566

美团前端常考手写面试题(边面边更)

helloworld1024fd

JavaScript

想会用synchronized锁,先掌握底层核心原理

华为云开发者联盟

开发 华为云 企业号十月 PK 榜

聊聊前端开发中的 Ghost Design 设计思路

Jerry Wang

前端开发 angular web开发 SAP 11月月更

动手实践丨使用华为云IoT边缘体验“边云协同”

华为云开发者联盟

云计算 华为云 企业号十月 PK 榜

一本书,带你走出Spring新手村

博文视点Broadview

阿里云E-HPC+i4p大内存实例,加速寻因生物单细胞数据分析效率

阿里云弹性计算

HPC

react源码中的生命周期和事件系统

flyzz177

React

react源码中的协调与调度

flyzz177

React

react源码中的hooks

flyzz177

React

CTO:我叫你画个技术图给我看看,咋就这么费劲呢?

程序员小毕

程序员 程序人生 CTO 画图软件 架构图

元宇宙场景技术实践|实现“虚拟人”自由

ZEGO即构

【电商实战00】用敏捷开发的思想,带你快速上手实战项目

王中阳Go

golang 高效工作 学习方法 11月月更 电商实战

OpenHarmony移植案例: build lite源码分析之hb命令__entry__.py

华为云开发者联盟

鸿蒙 芯片 华为云 源代码 企业号十月 PK 榜

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