PHP 8 新功能预览
PHP 计划将在今年(2020)的 12 月份发布 PHP 8,大版本号直接提升 1 点意味着有重大的升级,那 PHP 8 到底包含了哪些新功能呢?
JIT
通过 PHP 7.* 的不断优化,PHP 在 Zend VM 上已经运行得很快了,如果还想让 PHP 得到更进一步的性能提升,就只能上 JIT。
从我目前的认知来看,JIT ******的优势,就是代码编译结果不再是 Opcode 这种中间代码(IR,Intermediate Representation)在 Zend VM 上执行(因为只有 Zend VM 才认识 Opcode),而是直接编译成 DynASM (可直译为『动态汇编』),用 CPU 执行。
不过,大家不能认为 PHP 用上 JIT 之后,又会让你的网站运行速度有质的飞跃。能直接使用 CPU,只能让密集运算更上一个台阶,但实际上,因为大部分 PHP 项目都是 Web 项目,而 Web 项目的特点是,基本都是 IO 密集型(即处理数据库连接,读写文件等 IO 操作更多,处理计算较少),对于 Web 项目提升有限。
总结:PHP 以后也会有很多 CPU 密集型项目吧(大数据分析,视频解码,压缩等)。
PS. 如果是为了提升当前 Web 项目的性能,目前可能更好的方式还是 PHP 7.4 提供的预加载功能。
构造函数属性申明
英文原文叫 Constructor Property Promotion,Promotion 是『提升』之意,但直译总觉得很怪,所以私自把名称改了一下
用一个例子就能说明:
<?php
// PHP 8 之前
class Foo
{
private string $bar;
public function __construct(string $bar)
{
$this->bar = $bar;
}
}
// PHP 8 之后
class Foo
{
public function __construct(private string $bar)
{
}
}
总结:算个不错的语法糖吧。
Trait 抽象方法签名校验
<?php
trait FooTrait
{
abstract public function bar(int $baz);
}
class Foo
{
use FooTrait;
// 在 PHP 8 之前,这里是不会报错的
public function bar(string $baz)
{
}
}
总结:PHP 8 之前是不会报错的吗?有点吃惊
方法签名检查
<?php
class Foo
{
public function bar(int $baz)
{
}
}
class FooFoo extends Foo
{
// 在 PHP 8 之前,这里只会抛出 warning,之后都会变成 Fatal Error
public function bar(string $baz)
{
}
}
总结:PHP 8 之前居然只 Warning?持续吃惊
由负数做索引的数组添加新数组的行为变化
$a[-5] = 0;
$a[] = 0; // PHP 8 之前,此新增元素索引为 0,被改成了 -4
$a[] = 0; // 同理,目前索引将为 1,PHP 8 开始改为 -3
总结:这算冷知识吗,又可以给一些无聊的面试官提供无聊考题素材了。
联合类型
<?php
class Foo
{
public function __construct(private int|float $bar)
{
}
public function getBar(): int|float
{
return $this->bar;
}
}
总结:在 PHP 7.1 的年代,捕获异常就可以使用这种方式来针对某几种异常类型一起处理,居然到 PHP 8 的年代 参数 / 属性 / 返回值 类型才能这么写……
内置函数的参数错误将导致 TypeError
sort(1); // PHP 8 之前只报 Warning,而之后是 ThrowError
注:TypeError 是 Throwable,即可以用 try-catch 捕获
总结:举双手支持,你们团队里有多少是遇到 Warning: xxx() expects paramter n to be yyy, zzz given
这种错误从来不改的?
Throw 将变成表达式,而不是语句
表达式的使用比语句要更加自由,所以,当 throw 变成了表达式之后,下面的用法都将变成合法的:
mysql_connect(xxxx) or throw new Exception();
$a = $b ?? throw new Exception();
总结:小朋友,还记得表达式和语句的区别吗?
Weak Maps
又不知道怎么翻译了……但说明还是很容易的:
$map = new WeakMap();
$foo = new stdClass;
$map[$foo] = 1;
// PHP 8 之前,如果想用对象做数组的索引,只能用 SplObjectStorage
unset($foo);
// 如果是 PHP 8 之前,即使删除了 $foo,$map(SplObjectStorage)还是会保留
// $foo 的引用,使用不当可能会导致内存泄漏,而使用 WeakMap 后,一旦 $foo 被删
// $map 里相关的引用也没有了
总结:都说 WeakMap 是 PHP 7.4 的 WeakReference 的 Map 版,但我完全不知道 PHP 7.4 还有 WeakReference……
参数列表可保留最后参数后的逗号
function foo(
string $bar1,
string $bar2,
) {
}
总结:我记得 PHP 7.3 的时候就已经有这个功能了,后来仔细一看才发现原来 7.3 居然只是调用函数/方法的时候才可以保留最后参数后的逗号……
::class 可以用在对象上
没啥好多说的:
class Foo
{
}
$foo = new Foo;
echo $foo::class; // 跟 Foo::class 一样的结果
总结:PHP 5.5 的年代我就很奇怪为什么对象可以直接通过 ::
调用静态方法和静态属性,却不能直接调用 ::class
,你们呢?
属性
啥属性,其实就是注解,以后估计得这么写 Doctrine 的注解了,大家感受一下:
<?php
// use...
<<ORM\Entity>>
class User
{
}
以前虽然我们已经可以通过在注释里面添加注解来达到跟 Java 注解一样的目的,但以前是通过反射,先获取注释全部内容,再将注释按语法解析成有意义的单词来实现,而解析的过程是用 PHP 脚本自己实现的,比如 Doctrine/Annotation 库就是专门干这个的。现在好了,PHP 自己内置了之后,获取注解也变得更加容易,并且性能肯定也有极大提升。
总结:PHP 会不会因为这个功能的写法,反而失去更多粉丝。
?-> 操作符
你以前是否写过很多这样的代码:
$token = $tokenStorage->getToken();
if (null !== $token) {
$user = $token->getUser();
if (null $== $user) {
$name = $user->getUsername();
}
}
PHP 8 后舒服多了:
$name = $tokenStorage->getToken()?->getUser()?->getUsername();
总结:这样的语法糖赶紧给我来(chao)一打。
按名字指定参数
function foo($bar1 = 1, $bar2 = 2, $bar3 = 3, $bar4 = 4)
{
}
// 如果你只想指定第 4 个参数,其他参数都用默认值,那你就很尴尬了
foo(1, 2, 3, 5);
PHP 8 之后就可以写成
foo(bar4: 5);
总结:不知道 PHP 内置函数是不是也可以这么用。
Match 表达式
switch ($foo) {
case 1:
$bar = 'singular';
break;
case 2:
case 3:
$bar = 'plural';
break;
default:
$bar = 'unexpected';
}
// PHP 8 之后
$bar = match($foo) {
1 => 'singular',
2, 3 => 'plural',
default => 'unexpected';
};
Mixed 类型
PHP 文档里到处都可以看到 mixed 类型的参数,比如最常用的 var_dump
函数的参数,即表示参数可以为任何一种 PHP 类型的变量,到 PHP 8,mixed
正式成为类型关键字:
function foo(mixed $bar): mixed
{
}
// 注意上面的代码实际上是会保存的,mixed 并不包含 void 类型,所以函数必须有 return
总结:我想知道参数类型为 mixed
,跟不写参数类型到底有什么区别?
返回类型支持 static
没啥好多说的:
class Foo
{
public function test(): static
{
return new static();
}
}
总结:以前都支持返回 self 居然不支持返回 static ? 继续吃惊
子类 private 方法定义不受父类同名方法影响
我觉得标题已经能说清楚问题了,就不用举例了吧……
总结:以前居然会受影响,除了吃惊还是吃惊
支持无异常变量的 catch 表达式
try {
xxx();
} catch (Throwable) {
echo '发生了一个错误';
}
总结:没啥好总结的,就是好。
Stringable 接口
没啥好说的,如果你的类里面有 __toString
方法,自动就是 Stringable 了。
稳定的数组排序
针对保留 key 的数组排序来说的,比如下面这个数组的排序:
$arr = [ 'a' => 1, 'c' => 2, 'd' => 2, 'b' => 1];
asort($arr);
// PHP 8 之前
// 有时候是 ['a' => 1, 'b' => 1, 'c' => 2, 'd' => 2];
// 有时候是 ['b' => 1, 'a' => 1, 'd' => 2, 'c' => 2];
总结:这种冷门的 bug 是怎么发现的
更严格的算数类型检查
[] % [1]; // 这在 PHP 8 之前是可以的
$object + 1; // 这在 PHP 8 之前只会报 Warning
总结:感觉 PHP 8 是 Bug 大清理……
PDO 默认错误模式修改为抛异常
PHP 8 之前是 PDO 的错误模式是静默处理,即不报任何错误,PHP 8 终于改为默认抛出异常。
总结:回想刚开始用 PDO 来操作数据库,明明没报错,但就是不出查询结果的青涩年代。
@ 不再屏蔽严重错误
总结:是不是因为 @
被用来屏蔽错误信息,导致 PHP 不能像 Java 一样直接用 @
来做注解开始符。
ext-json 将不再独立为扩展
总结:的确,JSON 现在太常用了。
添加 get_resource_id 函数
总结:是不是很惊喜,居然以前也没有这个函数!那以前如果要获取一个资源的 id 是怎么做的呢?也不是没有办法:(int) $resource
。
新增三个字符串相关函数
这三个函数分别是:
str_contains('abc', 'ab'); // 是否包含
str_starts_with('abc', 'ab'); // 是否第二参数开头
str_ends_with('abc', 'ab'); // 是否第二参数结尾