写点什么

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

发布了 842 篇内容, 共 452.5 次阅读, 收获喜欢 1760 次。

关注

评论

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

GAIA数据集V1.10更新

云智慧AIOps社区

运维 AIOPS 数据集

原来,嵌入式BI方案的核心差异全在这,教你如何评估!

葡萄城技术团队

数据分析 BI 嵌入式软件 核心差异

深入浅出Redis丨阿里架构师的Redis实战心得,让你毫不费力的学习

Java浪潮

redis java程序员 java架构

作为Java程序员连Redis都不会?阿里架构师带你深入“解剖”Redis

Java浪潮

redis JAVA开发 java程序员

AI简报:Blind超分KernelGAN

AIWeker

人工智能 深度学习 机器视觉 5月月更 超分

如何制作优秀的产品说明手册?

小炮

产品说明手册

洞见科技纪凯:基于隐私计算的「客户增长」生态

洞见科技

金融科技 隐私计算

SpringCloud 中 Gateway 是如何关联对应的 GatewayFilterFactory 和 参数解析的

gin

Gateway SpringCould

Hacker 资讯 | 5 月上旬区块链黑客松活动汇总

One Block Community

区块链

黄吉:如何适配OpenHarmony自有音频框架ADM?

OpenHarmony开发者

OpenHarmony

报告解读下载 | 5月《中国数据库行业分析报告》重磅发布!精彩抢先看!

墨天轮

数据库 oracle opengauss TiDB 国产数据库

重复造轮子?TCP天然支持可靠传输为什么还需要基于UDP实现可靠传输?

Java全栈架构师

程序员 字节跳动 面试 TCP 网络

运维审计堡垒机哪款好?报价贵吗?

行云管家

网络安全 堡垒机 IT运维 运维审计

【OpenCV】配置OpenCV教程,OpenCV入门

恒山其若陋兮

5月月更

手把手推导Back Propagation

OneFlow

人工智能 神经网络 深度学习 Back Propagation 推导过程

毕业总结

凌波微步

架构训练营

进阶中的程序员:深入解析 Spring boot(含PDF文档分享)

Java浪潮

spring 编程 JAVA开发 springboot

什么是流动性池?(上)|流动性池的出现及名词解析

区块链前沿News

流动性 Hoo

[数据分析]-音频分析-BirdCLE-1

浩波的笔记

人工智能 AI 数据分析

导师男团来袭 | 开源之夏2022,与Alluxio一起探索数据编排的奇妙世界

Alluxio

开源 大学生 #开源项目 开源之夏 数据编排

这么好用的低延时直播,网易云信竟然将它开源了?!

网易云信

开源 WebRTC 音视频技术

借鉴这份Java进阶架构师之路的核心知识,我成功收获蚂蚁金服、阿里、美团等Offer

Java浪潮

Java 架构 java架构

阿里Java面试必问:JVM与性能优化+Redis+设计模式+分布式

Java浪潮

编程 架构

华为手机用户找歌不用愁,HarmonyOS 2版本更新上线“听歌识曲”

科技汇

DPDK背景和优势,赶紧学起来运用吧

C++后台开发

虚拟化 Linux服务器开发 DPDK C++后台开发 高性能网络

阿联酋航空与华为进一步深化合作伙伴关系

最新动态

CRM系统的18个关键功能

低代码小观

CRM 客户关系管理 企业管理系统 CRM系统 客户关系管理系统

云原生基础治理平台SchedulX V1.2.0正式发布,助力企业降本增效

星汉未来

运维 云原生 星汉未来

服务器CPU和普通CPU有什么区别?常用的服务器有六大区别

Finovy Cloud

深度学习 gpu GPU算力

堡垒机是服务器吗?两者有区别吗?

行云管家

运维 网络安全 服务器 堡垒机

网络协议之:memcached binary protocol详解

程序那些事

Java 网络协议 程序那些事 5月月更

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