PHP开源Hub
致力于开发者的提升

PHP 7.4 新特新速看,预加载了解一下~

PHP 7.4 ,下一个 PHP 7 较小的发布版,期望在 2019 年 11 月 28 日发布。因此,现在是时候让大家深入了解这个版本添加哪些新特性使 PHP 更快、更可靠。

虽然 PHP 7.4 显著地提升了性能和提高代码可读性,PHP 8 才将会是 PHP 性能真正的里程碑,这在 JIT inclusion 的提案显示已充分证明。

现在去无偿迁移

总之,今天我们将概览 PHP 7.4 最瞩目的特性和性能提升。在继续探索之前,你最好记住以下重要的时间节点:

  • 6 月 6 日:PHP 7.4 Alpha 1
  • 7 月 18 日:PHP 7.4 Beta 1 – Feature freeze(特性固化)
  • 11 月 28 日:PHP 7.4 GA Release

你可从 the official RFC page 查看全部新特性和功能。

PHP 7.4 发布日期:

PHP 7.4 将于 2019 年 11 月 28 日发布。这是 PHP 7 的下一个小版本,会再次提升性能,提高代码的可读性和可维护性。

PHP 7.4 有什么新功能?

在这边文章中,我们将讨论 PHP 7.4 最终版本中应该增加一些变化和特性:

抛弃 array_merge : PHP 7.4 在数组表达式中引入了扩展运算符

从 PHP 5.6 开始,参数解析 是一种解析数组并遍历到参数列表中的语法。要解析一个或遍历一个数组,必须以 …(三个点)作为前缀,如下所示:

function test(...$args) { var_dump($args); }
test(1, 2, 3);

现在  PHP 7.4 的 RFC 建议将这个功能扩展到数组定义中:

$arr = [...$args];

扩展操作符在数组表达式 第一个明显的优点是性能。RFC 文档

扩展操作符应该比 array_merge 性能更好。不仅是因为扩展操作符是一种语言结构,array_merge 是一个函数,还因为可以优化编译常量数组的性能。

扩展操作符一个重要的优点是支持任何可遍历的对象, array_merge 函数只支持数组。

下面是数组表达式中的参数解析示例:

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
var_dump($fruits);

如果你在 PHP 7.3 或更早版本中运行此代码,PHP 将抛出解析错误:

Parse error: syntax error, unexpected '...' (T_ELLIPSIS), expecting ']' in /app/spread-operator.php on line 3

相反,PHP 7.4 将返回一个数组:

array(5) {
    [0]=>
    string(6) "banana"
    [1]=>
    string(6) "orange"
    [2]=>
    string(5) "apple"
    [3]=>
    string(4) "pear"
    [4]=>
    string(10) "watermelon"
}

RFC 声明我们可以多次扩展同一个数组。而且,我们可以在数组中的任何地方使用扩展运算符语法,因为可以在扩展运算符之前或之后添加普通元素。所以,就像下面代码所示的那样:

$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$arr3 = [...$arr1, ...$arr2];
$arr4 = [...$arr1, ...$arr3, 7, 8, 9];

也可以将函数返回的数组直接合并到另一个数组:

function buildArray(){
    return ['red', 'green', 'blue'];
}
$arr1 = [...buildArray(), 'pink', 'violet', 'yellow'];

PHP 7.4 输出以下数组:

array(6) {
    [0]=>
    string(3) "red"
    [1]=>
    string(5) "green"
    [2]=>
    string(4) "blue"
    [3]=>
    string(4) "pink"
    [4]=>
    string(6) "violet"
    [5]=>
    string(6) "yellow"
}

我们也可以使用 生成器:

function generator() {
    for ($i = 3; $i <= 5; $i++) {
        yield $i;
    }
}
$arr1 = [0, 1, 2, ...generator()];

但是我们不允许合并通过引用传递的数组。 考虑以下的例子:

$arr1 = ['red', 'green', 'blue'];
$arr2 = [...&$arr1];

如果我们尝试按引用合并数组,则 PHP 会引发以下解析错误:

Parse error: syntax error, unexpected '&' in /app/spread-operator.php on line 3

无论如何,如果第一个数组的元素是通过引用存储的,则它们也将通过引用存储在第二个数组中。 这是一个例子:

$arr0 = 'red';
$arr1 = [&$arr0, 'green', 'blue'];
$arr2 = ['white', ...$arr1, 'black'];

这就是我们使用 PHP 7.4 所获得的:

array(5) {
    [0]=>
    string(5) "white"
    [1]=>
    &string(3) "red"
    [2]=>
    string(5) "green"
    [3]=>
    string(4) "blue"
    [4]=>
    string(5) "black"
}

The Spread operator 提案以 43 票对 1 票获得通过。

箭头函数 2.0 (短闭包)

对于 PHP 而言,匿名函数 被认为十分冗长并且难以使用和维护的。RFC 提出了更短并且语法更简洁的 * 箭头函数(短闭包),能够在很大程度上使我们的 PHP 代码更简洁。

考虑如下例子:

function cube($n){
    return ($n * $n * $n);
}
$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);
print_r($b);

PHP 7.4 允许使用更简洁的语法,上面的函数可以重写为如下:

$a = [1, 2, 3, 4, 5];
$b = array_map(fn($n) => $n * $n * $n, $a);
print_r($b);

目前,要感谢 use 语法,匿名函数 (闭包) 可以从父作用域里继承已经定义的变量:

$factor = 10;
$calc = function($num) use($factor){
    return $num * $factor;
};

但是在 PHP 7.4 中, 在父作用域里定义的变量被隐式捕获(隐式作用域绑定)了。如此一来,我们可用只用一行代码重写整个上面的函数:

$factor = 10;
$calc = fn($num) => $num * $factor;

我们可以像使用 use(变量) 一样,直接使用在父作用域里定义的变量,并且它也不会修改父作用域的变量。

新的语法对我们构建更可读可维护的代码带来了极大的改善。我们也可以使用参数和返回类型、默认值、变长参数列表(可变函数),可以传递或返回引用等等。然后呢,短闭包还可以被用作类方法,可以像常规一样使用 $this

RFC 已经以 51 票对 8 票通过了,所以我们可以期待在 PHP 7.4 新增功能里见到它。

空合并赋值操作符

在 PHP 7 中,当我们需要同时使用三元运算符和 isset() 时,合并运算符??)就可以派上用场了。如果第一个操作数是存在并且不为 NULL,则返回该操作数。否则返回第二个操作数。示例如下:

$username = $_GET['user'] ?? 'nobody';

这段代码很简单:获取请求参数,如果不存在,则设置一个默认值。它的意思很明确,但如果出现像下方这个来自 RFC 中示例这种更长的变量名呢?

$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';

从长远的角度看,这段代码可能有点难以维护。因此,为了帮助开发人员编写更直观的代码,这个 RFC 建议引入空合并赋值操作符??=)。因此,我们可以编写如下代码进行替代:

$this->request->data['comments']['user_id'] ??= 'value';

如果左侧的参数是 null,则使用右侧参数的值。请注意,当合并运算符是比较运算符时,??= 就是一个赋值运算符。

这项建议以 37:4 的票获得通过。

类型属性 2.0

参数类型声明(或类型提示)允许对将要传递给函数或者类方法的变量类型进行限定。该功能自 PHP 5 起可用,PHP 7.2 起可以使用对象作为数据类型。现在 PHP 7.4 通过添加 类属性类型声明 进一步扩展了类型提示。以下是一个基本的示例:

class User {
    public int $id;
    public string $name;
}

支持 void 与 callable 以外的所有类型

public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;

这项 RFC 解释了为什么不支持 void 和 callable 返回值的原因:

不支持 void 类型,因为它没有用到并且语义不明确。

不支持 callable 类型,因为其行为取决于上下文。

这样我们就可以安全地使用 boolintfloatstringarrayobjectiterableselfparent, 任何类或接口名称,并且可以为空 types (?type)。

类型可以用于静态属性:

public static iterable $staticProp;

也可以使用 var 标记:

var bool $flag;

可以设置默认属性值,当然必须与声明的属性类型匹配,但是只有可为空的属性可以具有默认的 null 值:

public string $str = "foo";
public ?string $nullableStr = null;

相同类型适用于单个声明中的所有属性:

public float $x, $y;

如果我们对属性类型进行错误处理会怎样? 考虑以下代码:

class User {
    public int $id;
    public string $name;
}

$user = new User;
$user->id = 10;
$user->name = [];

在上面的代码中,我们声明了字符串属性类型,但是我们将数组设置为属性值。 在这种情况下,我们将收到以下致命错误:

Fatal error: Uncaught TypeError: Typed property User::$name must be string, array used in /app/types.php:9

该 RFC 已以 70 票对 1 票获得批准。

弱引用

在这项 RFC 中,PHP 7.4 引入了 WeakReference (弱引用) 类型,这样开发者就可以保留对对象的引用,而这不会阻止对象本身被破坏。

目前,PHP 通过使用诸如 pecl-weakref 之类的扩展名来支持弱引用。 无论如何,新的 API 与记录的 WeakRef 类不同。

这是 一份简单的 demo 来自这项提议的作者 Nikita Popov

$object = new stdClass;
$weakRef = WeakReference::create($object);

var_dump($weakRef->get());
unset($object);
var_dump($weakRef->get());

第一个 var_dump 打印对象 object(stdClass)#1 (0) {} ,第二个 var_dump 打印引用为 NULL,因为所引用的对象已被销毁。

该 RFC 以 28 票对 5 票获得通过。

协变量返回和协变量参数

方差 是类层次结构的一个属性,描述了类型构造函数的类型如何影响 subtypes。 通常,类型构造函数可以是:

  • Invariant: 如果超类型的类型约束子类型的类型。
  • Covariant:如果保留类型的顺序(类型从更具体到更一般)。
  • Contravariant:如果它颠倒了顺序(类型从更通用到更具体地排序)。

目前,PHP 的参数和返回类型大部分不变,只有少数例外。 该 RFC 建议允许在参数类型和返回类型上进行协方差和协变,并提供一些代码示例。

这是 协变量返回 的一个简单例子:

interface Factory {
    function make(): object;
}

class UserFactory implements Factory {
    function make(): User;
}

这个是 协变量参数 一个示例:

interface Concatable {
    function concat(Iterator $input); 
}

class Collection implements Concatable {
    // accepts all iterables, not just Iterator
    function concat(iterable $input) {/* . . . */}
}

请参阅 RFC 以更详细地了解 PHP 7.4 协变量返回和协变量参数。

该 RFC 以 39 对 1 票获得通过。

预加载

这项提议 来自 Dmitry Stogov ,这是我们的受支持的提议之一,因为它可以显着提高 PHP 的性能。预加载 是在模块初始化时将库和框架加载到 OPCache 的过程,详细了解 PHP 生命周期 。

PHP 生命周期 (资源镜像: PHP Internals)

用 Dmitry 的话来说,预加载是这样工作的:

在服务器启动时(在运行任何应用程序代码之前),我们可以将一组 PHP 文件加载到内存中,并使它们的内容 永久可用 给该服务器将服务的所有后续请求。 与内部实体完全一样,这些文件中定义的所有函数和类也可用于开箱即用的请求。

这些文件在服务器启动时加载,在任何应用程序之前执行,并且对以后的任何请求均可用。 就性能而言,这很棒。

预加载由特定的 php.ini 指令控制:opcache.preload。 该指令指定在服务器启动时要编译和执行的 PHP 脚本。 此文件可用于预加载其他文件,包括它们或通过 opcache_compile_file() 函数(有关更多信息,请参见 [PHP 文档](https://www.php.net/manual/en/function.opc… -file.php))。

但是有一个缺点。 实际上,RFC 里有明确声明:

预加载的文件将永远保留在 opcache 内存中。 不重新启动另一台服务器,对其相应源文件的修改将不会生效。

但是,在预加载的文件中定义的所有函数将被永久加载到 PHP 函数和类表中,并且对于以后的每个请求均可用。 即使这些改进可能有很大的不同,也会带来良好的性能改进。

您可以在官方的 预加载 RFC 页面 上阅读有关预加载的限制和例外的更多信息。

新的自定义对象序列化机制

这是 Nikita Popov 的另一项提议

当前,我们有两种不同的机制可以在 PHP 中对对象进行自定义序列化:

  • __sleep() 和 __wakeup() 魔术方法
  • Serializable 接口

根据 Nikita 的说法,这两个选项都存在导致复杂且不可靠的代码的问题。 您可以在 RFC 中深入研究此主题。 在这里我只是提到新的序列化机制应该通过提供两种新的魔术方法__serialize() 和 __unserialize() 来解决这些问题,这两种方法结合了两个现有机制。

该提案以 20 票对 7 票获得通过。

已废弃

PHP 7.4 不推荐使用以下功能。 要获得更全面的弃用列表,请查看 PHP 7.4 升级说明

更改串联运算符的优先级

当前,在 PHP 中,+ 和 - 算术运算符以及 . 字符串运算符保持关联性并具有相同的优先级。(阅读更多相关信息运算符优先级)

例如,考虑以下行:

echo "sum: " . $a + $b;

在 PHP 7.3 中,此代码产生以下警告:

Warning: A non-numeric value encountered in /app/types.php on line 4

这是因为从左到右评估了串联。 与编写以下代码相同:

echo ("sum: " . $a) + $b;

这项 RFC 建议更改运算符的优先级,给 . 赋予比 + 和 - 运算符低的优先级,以便总是在字符串连接之前执行加法和减法。 该行代码应等效于以下内容:

echo "sum: " . ($a + $b);

这是一个两步建议:

  • 从 7.4 版开始,PHP 在遇到带有 + ,- 和 . 的非括号表达式时应发出弃用通知。
  • 这些运算符的优先级的实际更改应在 PHP 8 中添加。

两项提议均以绝大多数票获得批准。

弃用左联想三元运算符

在 PHP 中,与许多其他语言不同,三元运算符是左关联的。 根据 Nikita Popof 的说法,这对于在不同语言之间进行切换的开发者可能会造成混淆。

当前,在 PHP 中,以下代码是正确的:

$b = $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : 'other';

解释为:

$b = (($a == 1 ? 'one' : $a == 2) ? 'two' : $a == 3) ? 'three' : 'other';

这可能会导致错误,因为这可能不是我们打算要做的。 因此,该 RFC 建议弃用并删除三元运算符的左关联性,并强制开发人员使用括号。

这是另外两个步骤的建议:

  • 从 PHP 7.4 开始,不显式使用括号的嵌套三元将抛出弃用警告。
  • 从 PHP 8.0 开始,将出现编译时错误。

该提案以 35 到 10 票获得批准。

在 Docker 中安装和运行 PHP 7.4

想在 Docker 上试试吗?幸运的是在 Docker 环境下你不需要再手动编译和配置 PHP 7.4 了 。如果你已经安装了 Docker , 那只需要花几秒钟安装这个非官方的 PHP-FPM 7.4 Docker 镜像 就可以在命令行中进行测试了。

如果你想要运行 PHP 7.4 的代码到你的浏览器中,那你还需要给 Docker 安装 Nginx 或 Apache 镜像。不用担心,只要按照 开发指南。 将示例命令拷贝粘贴到命令行中并运行,就可以了。

摘要

在这篇文章中我们简单介绍了 PHP 7.4 发行版大量的更新和新增功能。如果你还想要了解完整的功能列表和正式的 RFC 官方文档,可以参考这些资源:

原文地址:https://kinsta.com/blog/php-7-4/

译文地址:https://learnku.com/php/t/37324

赞(0) 打赏
未经允许不得转载PHP开源Hub » PHP 7.4 新特新速看,预加载了解一下~

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

PHP开源Hub-致力于互联网开发者的成长

技术群聊软文发表

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏