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 类型,因为其行为取决于上下文。
这样我们就可以安全地使用 bool, int, float, string, array, object, iterable, self, parent, 任何类或接口名称,并且可以为空 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 生命周期 。
[
](https://cdn.learnku.com/uploads/images/201911/28/1/YB6uafZkoe.png!large)
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 镜像 就可以在命令行中进行测试了。
[
](https://cdn.learnku.com/uploads/images/201911/28/1/To5Rmy73YY.png!large)
如果你想要运行 PHP 7.4 的代码到你的浏览器中,那你还需要给 Docker 安装 Nginx 或 Apache 镜像。不用担心,只要按照 开发指南。 将示例命令拷贝粘贴到命令行中并运行,就可以了。
摘要
在这片文章中我们简单介绍了 PHP 7.4 发行版大量的更新和新增功能。如果你还想要了解完整的功能列表和正式的 RFC 官方文档,可以参考这些资源:
© 著作权归作者所有
发表评论