php 7.4.0 发布了,此版本标志着 php 7 系列的第四次特性更新。

看了英文手册后,发现其进行了许多改进,并带来了一些新特性,现在将这些新特性您:

 

1.typed properties 类型属性

类属性现在支持类型声明,以下示例将强制 $user-> id 只能分配 int 值,而 $user-> name 只能分配 string 值。

<?php

class user {

    public int $id;

    public string $name;

}

?>

● 它们自php 7.4起可用。

● 它们只在类中可用,并且需要访问修饰符:public、protected、private、var。

● 除了void和callable之外,所有类型都是允许的。

php是我们喜欢和讨厌的一种动态语言,它将强制类型转换做的太好,有时也会引起反作用。假设您在期望整数的地方传递了一个字符串,php将尝试自动转换该字符串:

class bar

{

    public int $i;

}

$bar = new bar;

$bar->i = '1'; // 1

如果不喜欢这种行为,可以通过声明严格类型来禁用它:

declare(strict_types=1);
$bar = new bar;

$bar->i = '1'; // 1

fatal error: uncaught typeerror: 

typed property bar::$i must be int, string used

 

2.arrow functions 箭头函数

箭头函数提供了用于定义具有隐式按值作用域绑定的函数的简写语法。

<?php

$factor = 10;

$nums = array_map(fn($n) => $n * $factor, [1, 2, 3, 4]);

// $nums = array(10, 20, 30, 40);

?>

● 自php 7.4起可用

● 他们以fn关键字开头

● 它们只能有一个表达式,即return语句

● 不允许return关键字

● 参数和返回类型可以是类型提示

您没看错:短闭包只能有一个表达式。这意味着您不能包含多行。

理由如下:

简短闭包的目标是减少冗长。fn当然在所有情况下都比function短。然而,如果您处理的是多行函数,那么使用短闭包所获得的好处就更少。

毕竟,按照定义,多行闭包已经更加冗长;因此能够跳过两个关键字(function和return)不会有太大的区别。

 

3.limited return type covariance and argument type contravariance 有限返回类型协变与参数类型逆变

仅当使用自动加载时,才提供完全协变/逆变支持。在单个文件中,只能使用非循环类型引用,因为所有类在被引用之前都必须可用。

<?php

class a {}

class b extends a {}

class producer {

    public function method(): a {}

}

class childproducer extends producer {

    public function method(): b {}

}

?>

 

4.unpacking inside arrays 打包内部数组

<?php

$parts = ['apple', 'pear'];

$fruits = ['banana', 'orange', ...$parts, 'watermelon'];

// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

?>

 

5.numeric literal separator 数值文字分隔符

数字文字可以在数字之间包含下划线。

<?php

6.674_083e-11; // float

299_792_458;   // decimal

0xcafe_f00d;   // hexadecimal

0b0101_1111;   // binary

?>

限制

唯一的限制是数字文字中的每个下划线必须直接位于两个数字之间。这条规则意味着下面的用法都不是有效的数字文字:

_100; 

100_;       

1__1;       

1_.0; 1._0; 

0x_123;     

0b_101;     

1_e2; 1e_2; 

php功能不受影响

在数字文字的数字之间添加下划线不会改变其值。下划线在词法分析阶段被删除,因此运行时不受影响。

var_dump(1_000_000); // int(1000000)

 

6.weak references 弱引用

弱引用可以保留对对象的引用,不会阻止对象被销毁。

弱引用允许保留对对象的引用,而该对象不会阻止对象被销毁;它们对于实现类似缓存的结构非常有用。

原则上,弱引用对象并不复杂,只需要(ab)使用zend或下面的层,因为我们不直接支持它。

final class weakreference {

    public static function create(object $object) : weakreference;
  
    public function get() : ?object;

}

 

7.allow exceptions from __tostring() 允许从 __tostring() 抛出异常

现在允许从 __tostring() 引发异常,以往这会导致致命错误,字符串转换中现有的可恢复致命错误已转换为 error 异常。

7.4以前禁止从__tostring()抛出异常,如果__tostring()异常,将导致致命错误。

从技术角度来看,这种限制最终是无效的,因为字符串转换期间的异常仍然可以由将可恢复错误转换为异常的错误处理程序触发:

set_error_handler(function() {

    throw new exception();

});

try {

    (string) new stdclass;

} catch (exception $e) {

    echo "(string) threw an exception...\n";

}

另外,将“不能转换为字符串”和“__tostring()必须返回一个字符串值”可恢复的致命错误转换为正确的错误异常,这与php 7中建立的错误策略一致。

 

8.opcache preloading opcache 预加载

新增 opcache 预加载支持。

在php 7.4中,添加了对预加载的支持,这是一个可以显著提高代码性能的特性。

简而言之,这是它的工作方式:

● 为了预加载文件,您需要编写一个自定义php脚本

● 该脚本在服务器启动时执行一次

● 所有预加载的文件在内存中都可用于所有请求

● 在重新启动服务器之前,对预加载文件所做的更改不会产生任何影响

虽然预加载是建立在opcache之上的,但它并不是完全一样的。opcache将获取您的php源文件,将其编译为“ opcodes”,然后将这些编译后的文件存储在磁盘上

您可以将操作码看作是代码的底层表示,在运行时很容易解释。因此,opcache会跳过源文件和php解释器在运行时实际需要之间的转换步骤。巨大的胜利!

但我们还有更多的收获。opcached文件不知道其他文件。如果类a是从类b扩展而来的,那么仍然需要在运行时将它们链接在一起。此外,opcache执行检查以查看源文件是否被修改,并将基于此使其缓存失效。

因此,这就是预加载发挥作用的地方:它不仅将源文件编译为操作码,而且还将相关的类、特征和接口链接在一起。然后,它将这个“已编译”的可运行代码blob(即:php解释器可以使用的代码)保存在内存中

现在,当请求到达服务器时,它可以使用已经加载到内存中的部分代码库,而不会产生任何开销。

为了进行预加载,开发人员必须告知服务器要加载哪些文件。这是用一个简单的php脚本完成的,确实没有什么困难。

规则很简单:

● 您提供一个预加载脚本,并使用opcache.preload命令将其链接到您的php.ini文件中。

● 您要预加载的每个php文件都应该传递到opcache_compile_file(),或者在预加载脚本中只需要一次。

假设您想要预加载一个框架,例如laravel。您的脚本必须遍历vendor/laravel目录中的所有php文件,并将它们一个接一个地添加。

在php.ini中:

opcache.preload=/path/to/project/preload.php

这是一个虚拟的实现:

$files = /* 要预加载的文件数组 */;

foreach ($files as $file) {

    opcache_compile_file($file);

}

有一个警告!为了预加载文件,还必须预加载它们的依赖项(接口,特征和父类)

如果类依赖项有任何问题,则会在服务器启动时通知您:

can't preload unlinked class

illuminate\database\query\joinclause: 

unknown parent 

illuminate\database\query\builder

这不是一个致命的问题,您的服务器可以正常工作。但你不会得到所有你想要的预加载文件。

幸运的是,还有一种确保链接文件也被加载的方法:您可以使用require_once代替opcache_compile_file,让已注册的autoloader(可能是composer的)负责其余的工作。

$files = /* 要预加载的文件数组 */;

foreach ($files as $file) {

    require_once($file);

}

还有一些需要注意的地方。例如,如果您试图预加载laravel,那么框架中的一些类依赖于其他尚不存在的类。例如,文件系统缓存类\ lighting \ filesystem \ cache依赖于\league\flysystem\cached\storage\abstractcache,如果您从未使用过文件系统缓存,则可能无法将其安装到您的项目中。

#有效吗?

这当然是最重要的问题:所有文件都正确加载了吗?您可以简单地通过重新启动服务器来测试它,然后将opcache_get_status()的输出转储到php脚本中。您将看到它有一个名为preload_statistics的键,它将列出所有预加载的函数、类和脚本;以及预加载文件消耗的内存。

#性能

现在到最重要的问题:预加载真的能提高性能吗?

答案是肯定的:我进行了一些基准测试。

有趣的是,您可以决定仅预加载代码库中经常使用的类。基准测试显示,只加载大约100个热门类,实际上可以获得比预加载所有类更好的性能收益。预加载全部类,性能提升13%,而预加载热门类,则提升有17%。

当然,应该预加载哪些类取决于您的项目。明智的做法是在开始时尽可能多地预加载。

此外还有一些弃用,以及从核心中删除一些扩展,详情查看英文原版手册:

https://www.php.net/manual/zh/migration74.new-features.php