认识PHP8
PHP 团队于2020年11月26日宣布 PHP 8 正式发布!这意味着将不会有 PHP 7.5 版本。PHP8 目前正处于非常活跃的开发阶段,所以在接下来的几个月里,情况可能会发生很大的变化。我也分享一些研究PHP 8 的心得,希望PHPer大家一起共同进步。首先说一下最受关注的JIT。
JIT
由于 PHP 8 是一个新的大版本,因此升级版本,代码被破坏的可能性更高。如果项目始终保持运行 PHP 的最新版本,那么升级相对来说就会轻松很多,因为在 7. * 版本中,大多数重大更改均已弃用。除重大更改外,PHP 8 还带来了一些不错的新功能,比如说 JIT 编译器 , 联合类型 , 属性,以及更多。很多人可能对JIT有很深的误解,觉得引入JIT之后性能就能提高10倍跟V8平起平坐了,事实上不是这样的。JIT技术的水很深,动态语言的JIT尤其困难,V8的诞生几乎可以说是一个技术奇迹。以PHP社区的技术水平,我谨慎地不看好他们解决这个问题的能力,毕竟Facebook的HHVM也没有完全解决,最后是靠Hacklang补全PHP的语法功能之后才基本圆满解决的。
动态语言的JIT本质要解决的问题之中,生成汇编只是一小部分,对于弱类型和动态类型语言来说,优化内存布局也是重点。例如,对于JavaScript和Python来说,以前对象内部是一个HashMap,这种数据结构的访问效率比较低,导致访问对象的每个属性都很慢,在JIT之后会将它优化成类似C++的平铺式的布局,将属性的值按顺序放在特定的位置上,这就带来一些新的要求:
1. 没有类型标注的情况下,JIT只能猜测类型而无法肯定,那么使用优化的类型布局之前需要进行额外的检测,判断是否的确为预想的类型;
2. 属性的类型也需要进一步推测,使用时也需要检验;
3. JavaScript、Python乃至PHP都支持在对象创建之后为它添加新的属性。之前符合推测的类型后来添加或者删除了属性,要怎么处理?
除此之外,调用函数时候如何优化调用开销也是一个重点,本质上跟优化对象的内存布局是类似的,可以将传入参数看成是构建一个有多个属性的对象,每个属性的类型不同。局部变量也需要有选择性地优化到寄存器、栈和堆当中。
PHP在这里的优势是支持类型标注,缺点是所有Hacklang里面修改掉的部分:
1. 不支持泛型,尤其是array类型不支持泛型。将一个变量类型标注为array几乎没有任何帮助,PHP中的array可以是顺序表也可以是hashmap,还可以混着,value的类型也不确定,这些都对类型优化有很高要求。Hacklang就推荐废掉array改用vector等几个确定类型且支持泛型的数据结构。
2. reference这个功能,这个功能非常容易成为内存布局优化的障碍,也会阻碍JIT生成高效代码,尤其是数组中可以存储reference这件事,JIT编译器完全无法从字面上判断某条对array元素赋值的语句是否会影响环境中的其它变量的值。这也是为什么Hacklang直接删掉了这个功能。
3. 其他参考Hacklang的变更
之前版本(PHP7)抠解释器实现带来的性能优化也会是一个阻碍,JIT的时候这些都得放弃掉,因为内存布局不一样了,这样可能导致最初的时候许多应用JIT反而变慢。所以,PHP8如果解决不了这些问题,最大的可能是许多microbenchmark速度大幅上升,但整体应用性能持平,自娱自乐。
联合类型
考虑到 PHP 动态语言类型的特性,现在很多情况下,联合类型都是很有用的。联合类型是两个或者多个类型的集合,表示可以使用其中任何一个类型。
public function foo(Foo|Bar $input): int|float;
联合类型中不包含 void
,因为 void
表示的含义是 “根本没有返回值”。 另外,可以使用 |null
或者现有的 ?
表示法来表示包含 nullable
的联合体 :
public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void;
属性
属性在其他语言中通常被称为 注解 ,提供一种在无需解析文档块的情况下将元数据添加到类中的方法。
use App\Attributes\ExampleAttribute; <<ExampleAttribute>>
class Foo
{
<<ExampleAttribute>>
public const FOO = 'foo'; <<ExampleAttribute>>
public $x; <<ExampleAttribute>>
public function foo(<<ExampleAttribute>> $bar) { }
}
新增 static 返回类型
尽管已经可以返回 self,但是 static 直到 PHP 8 才是有效的返回类型 。考虑到 PHP 具有动态类型的性质,此功能对于许多开发人员将非常有用。
class Foo
{
public function test(): static
{
return new static();
}
}
新增 mixed
类型
有人可能将其称为必要的邪恶:mixed
类型让许多人感觉十分混乱。然而,有一个很好的论据支持去实现它:缺少类型在 PHP 中会导致很多情况:
- 函数不返回任何内容或返回空值
- 我们需要多种类型的一种类型
- 我们需要的是 PHP 中不能进行类型提示的类型
因为上述原因,添加 mixed
类型是一件很棒的事儿。mixed
本身代表下列类型中的任一类型:
array
bool
callable
int
float
null
object
resource
string
请注意,mixed 不仅仅可以用来作为返回类型,还可以用作参数和属性类型。
另外,还需要注意,因为 mixed 类型已经包括了 null,因此 mixed 类型不可为空。下面的代码会触发致命错误:
// 致命错误:混合类型不能为空,null已经是混合类型的一部分。
function bar(): ?mixed {}
throw
表达式
将 throw
从一个语句更改为一个表达式,这使得可以在很多新地方抛出异常:
$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
允许对对象使用 ::class
一个很小但是很有用的新特性:现在可以在对象上使用 :: class ,而不必在对象上使用 get_class() ,它的工作方式跟 get_class() 相同。
$foo = new Foo(); var_dump($foo::class);
Non-capturing catches
在 PHP 8 之前,无论何时你想要捕获一个异常,你都需要先将其存储到一个变量中,不管这个变量你是否会用到。通过 Non-capturing catches 你可以忽略变量,所以替换下面的代码:
try {
// 执行错误代码段
} catch (MySpecialException $exception) {
Log::error("错误");
}
你现在可以这么做:
try {
// 执行错误代码段
} catch (MySpecialException) {
Log::error("错误");
}
请注意,必须始终指定类型,不允许将 catch
留空,如果你想要捕获所有类型的异常和错误,需要使用 Throwable
作为捕获类型。
新增 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
重新分类的错误信息
许多以前仅触发警告或通知的错误已转换为适当的错误。以下警告已更改。
- 变量未定义:Error 异常代替通知
- 数组索引未定义:警告代替通知
- 除以零:DivisionByZeroError 异常代替警告
- 尝试添加 / 移除非对象的属性 '% s' :Error 异常代替警告
- 尝试修改非对象的属性 '% s' :Error 异常代替警告
- 尝试分配非对象的属性 '% s' :Error 异常代替警告
- 从空值创建默认对象:Error 异常代替警告
- 尝试获取非对象的属性 '% s' :警告代替通知
- 未定义的属性:% s::$% s:警告代替通知
- 无法添加元素到数组,因为下一个元素已被占用:Error 异常代替警告
- 无法在非数组变量中销毁偏移量:Error 异常代替警告
- 无法将标量值用作数组:Error 异常代替警告
- 只有数组和 Traversables 可以被解包:TypeError 异常代替警告
- 为 foreach () 提供了无效的参数:TypeError 异常代替警告
- 偏移量类型非法:TypeError 异常代替警告
- isset 或 empty 中的偏移量类型非法:TypeError 异常代替警告
- unset 中的偏移量类型非法:TypeError 异常代替警告
- 数组到字符串的转换:警告代替通知
- 资源 ID#% d 用作偏移量,转换为整数 (% d):警告代替通知
- 发生字符串偏移量转换:警告代替通知
- 未初始化的字符串偏移量:% d:警告代替通知
- 无法将空字符串分配给字符串偏移量:Error 异常代替警告
- 提供的资源不是有效的流资源:TypeError 异常代替警告
@ 运算符不再使致命错误不提醒
@符是一个偷懒解决问题的办法,此更改可能会使 PHP 8 之前的版本被 @ 隐藏的错误再次显示出来。请确保在生产服务器上设置了 display_errors=Off !
默认错误报告级别
现在的默认错误报告级别是 E_ALL 而不是之前的除 E_NOTICE 和 E_DEPRECATED 的所有内容。这意味着可能会弹出许多错误,这些错误以前曾被忽略,尽管在 PHP 8 之前的版本中可能已经存在。
默认 PDO 错误模式
这个改动很坑,PDO 的默认错误模式改为静默。这意味着当出现 SQL 错误时,除非开发人员实现了自己的错误处理,否则不会发出任何错误或警告,也不会引发任何异常。
串联优先级
在 PHP 7.4 中已废弃,在8.0开始生效。如果你像这样子书写:
echo "sum: " . $a + $b;
PHP 以前会如是理解:
echo ("sum: " . $a) + $b;
PHP 8 :
echo "sum: " . ($a + $b);
暂时就讲这些比较有用的新特性吧,一些不常用的就不浪费大家时间了。
关于万众期待的JIT,我还想说一些,JIT会让我的项目更快吗?
很有可能并不明显。也许不是我们期望的答案:在一般情况下,用PHP编写的应用程序是I/O绑定的,然而JIT在CPU绑定的代码上工作得最好。
关于I/O绑定和CPU绑定最简单的说法是:
- 如果我们能够改进(减少、优化)它所做的I/O,那么一段I/O绑定的代码将会运行得更快。
- 如果我们能够改进(减少、优化)CPU正在执行的指令,或者(神奇地)提高CPU的时钟速度,那么一段CPU限制的代码就会运行得更快。
- 一段代码或一个应用程序可以是I/O绑定、CPU绑定,或者与CPU和I/O同等绑定。
- 一般来说,PHP应用程序往往是I/O绑定的——减慢它们速度的是它们正在执行的I/O——连接、读取和写入数据库、缓存、文件、套接字等等。
PHP实际上相当快,它是世界上解释速度最快的语言之一。Zend VM调用与I/O无关的函数,和在机器代码中进行相同的调用之间,没有显著的区别。而PHP的瓶颈也从来不是其他的,正是I/O。
所以JIT好像没什么用?
其实不然,引入JIT总体来讲是一个积极正面的发展:
- 目前已经很难通过常规手段提升 PHP 的性能,JIT 基本上是目前性能提升的唯一手段;
- JIT 带来的性能提升可以让 PHP 在更多使用场景( CPU 密集)中发挥作用;
- 可以使用 PHP 来开发内置函数,而不用担心性能方面的问题。这一方面可以加速语言的发展(更多PHPer可以参与进来),同时也可以减少目前使用 C 开发内置函数,容易出现的内存管理、溢出等问题。
JIT的引入,对整个语言的使用场景扩展,及语言生态发展有很深远的意义。语言可以有局限,但是人拥有无限可能。许多PHPer把自己局限在web一个角落内里。JIT的引入,现在人人都可以去拥抱PHP带来的转变与生态:Swoole解决了IO密集场景问题,JIT解决了运算密集场景问题,未来PHP的发展更让人期待。
认识PHP8的更多相关文章
- php8.0正式版新特性和性能优化学习
前言 PHP团队宣布PHP8正式GA(链接).php的发展又开启了新的篇章,PHP8.0.0版本引入了一些重大变更及许多新特性和性能优化机制.火速学习下~ JIT(Just in Time Compi ...
- PHP8开启PHPStorm + Xdebug3
下载Xdebug 需要下载对应php版本xdebug 否则对加载xdebug失败 https://xdebug.org/download 我的是PHP版本 为php8.0.3-nts-x64 安装xd ...
- ThinkPHP V6.0.12在php8.1下验证码出现问题
一.问题描述 1.项目需求要求使用PHP8.1.*版本 2.运行程序发现验证码不生效报错如下: 二.错误描述 1.报错信息得出:从浮点(数字)到整数的隐式转换将失去精度 三.解决流程 1.找到报错文件 ...
- PHP8中match新语句的操作方法
PHP8 新出的一个语法很好用,就是 match 语句.match 语句跟原来的 switch 类似,不过比 switch 更加的严格和方便 原来的 switch 语句代码如下: 1 function ...
- PHP8年开发经验原创开发文档教程
订阅微信公众号: gzgwgas 每天为你分享PHP开发经验,坚决不踩坑,坚决不入坑. 微信扫码,关注公众号有惊喜!
- Docke 搭建 apache2 + php8 + MySQL8 环境
Docker 安装 执行 Docker 安装命令 curl -fsSL https://get.docker.com/ | sh 启动 Docker 服务 sudo service docker st ...
- uva 1599 ideal path(好题)——yhx
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABGYAAAODCAYAAAD+ZwdMAAAgAElEQVR4nOy9L8/0ypH/Pa8givGiyC
- PHP 调用 Go 服务的正确方式 - Unix Domain Sockets
* { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...
- SQL注入之重新认识
i春秋作家:anyedt 原文来自:https://bbs.ichunqiu.com/thread-41701-1-1.html 引言 作为长期占据 OWASP Top 10 首位的注入,认识它掌握它 ...
随机推荐
- 转:浅谈HTTP中Get、Post、Put与Delete的区别
1.GET请求会向数据库发索取数据的请求,从而来获取信息,该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改.增加数据,不会影响资源的内容,即该请求不会产生副作用.无论进行多少次操 ...
- PyQt(Python+Qt)学习随笔:Qt Designer中部件的焦点策略focusPolicy设置
在Qt Designer中可以设置部件的焦点策略,部件的焦点策略属性取值范围由枚举类型Qt.FocusPolicy来定义,该枚举类型及其含义如下表所示: 注意:经老猿测试鼠标轮滚动获取焦点,只有在鼠标 ...
- RedHat操作指令第4篇
top(查看动态进程运行情况) top 是一个更加有用的命令,可以监视系统中不同的进程所使用的资源.它提供实时的系统状态信息. 显示进程的数据包括 PID.进程属主.优先级.%CPU.%memory等 ...
- python 读取excel表格内不同类型的数据
不同类型数据对应值: #coding=utf-8 import os import xlrd from datetime import datetime,date newparh = os.chdir ...
- AcWing 199. 余数之和
\(\sum_{i = 1}^{n} k \bmod i = n * k - \sum_{i = 1}^{n} \lfloor k / i \rfloor * i\) 显然,\(\lfloor k / ...
- hashmap底层:jdk1.8前后的改变
将hashmap和currenthashmap放一块进行比较,是因为二者的结构相差不多,只不过后者是线程安全的. 首先说hashmap,在jdk1.8之前,hashmap的存储结构是数组+链表的形式, ...
- Layui事件监听(表单和数据表格)
一.表单的事件监听 先介绍一下几个属性的用法 1.lay-filter 事件过滤器 相当于选择器,layui的专属选择器 2.lay-verify 验证属性 属性值可以是 :required必填项, ...
- Spring AOP的理解(通俗易懂)。
转载 原文链接:http://www.verydemo.com/demo_c143_i20837.html 这种在运行时,动态地将代码切入到类的指定方法.指定位置上的编程思想就是面向切面的编程. 1. ...
- 简单的一段css代码让全站变灰,网站哀悼代码
为表达全国各族人民对抗击新冠肺炎疫情斗争牺牲烈士和逝世通报的深切哀悼,国务院今天发布公告,决定2020年4月4日举行全国性哀悼活动.在此期间,全国和驻外使馆下半旗致哀,全国停止公共娱乐活动,4月4日1 ...
- Bootstrap 的基本使用
一.Bootstrap简介 Bootstrap 是目前受欢迎的前端框架之一,是基于HTML,CSS,JavaScript的,它简洁灵活,使web开发更加快捷 中文官网:http://www.bootc ...