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
类型,因为其行为取决于上下文。
这样我们就可以安全地使用 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生命周期 。
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 官方文档,可以参考这些资源:
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
前排支持
前排支持
我先来, php是世界上最美的语言
Nice,一直期待的箭头函数终于来了。
没什么有用的更新
买个板凳,类属性声明这个好,箭头函数
~越来越js了