PHP 7.4 有哪些新功能?为什么PHP8值得期待?
事实上,尽管 PHP 7.4 显着提高了性能并提高了代码可读性,但既然 JIT 包含建议已获得批准,PHP 8 仍然将是 PHP 性能的真正里程碑。
无论如何,今天我们将介绍 PHP 7.4 中一些最有趣的功能和变化。在阅读本文之前,请务必保存以下日期:
6 月 6 日:PHP 7.4 Alpha 1
7 月 18 日:PHP 7.4 Beta 1 – 功能冻结
11 月 28 日:PHP 7.4 GA 发布
您可以请参阅 RFC 官方页面上的功能和附加组件的完整列表。
PHP 7.4 发布日期:
PHP 7.4 计划于 2019 年 11 月 28 日发布。这是 PHP 7 的下一个次要版本,应该会再次提高性能并使您的代码更具可读性/可维护性。
PHP 7.4 中有哪些新功能?
本文讨论了 PHP 7.4 最终版本中应添加到该语言中的一些更改和功能:
- 支持数组内展开 - 展开运算符数组扩展
- 箭头函数 2.0(较短的闭包)
- NULL 连接运算符
- 弱引用
- 协变返回和逆变参数
- 预加载
- 新的自定义对象序列化机制
性能改进,数组表达式扩展运算符在…中引入Na自 PHP 5.6 起,参数解包 是将字符串和 Traversable 解包到参数列表中的语法。要解压数组或 Traversable,它必须以 ...(3 个点)作为前缀,如下例所示: function test(...$args) { var_dump($args); }
test(1, 2, 3);
function test(...$args) { var_dump($args); }
test(1, 2, 3);
但是,PHP 7.4 RFC 建议将此功能扩展到数组来定义:
$arr = [...$args];
Spread 操作符的第一个优点是性能,RPC文档指出:
Spread操作符应该比array_merge
有更好的性能。不仅扩展运算符是语法结构,array_merge
方法也是。即使在编译时,常量字段也会进行优化以实现高效率。
Spread 运算符的一个重要优点是它支持任何可遍历的对象,而 array_merge
函数仅支持数组。以下是使用 Spread 运算符的数组中的参数示例:
$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"
}
箭头函数 2.0(短闭包)
在 PHP 中,匿名函数被认为非常冗长且难以实现和维护, RFC建议引入更简单、更清晰的箭头函数语法(或简短的结论),以便我们可以简洁地编写代码。 PHP 7.4之前:
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);
目前,由于语言结构的原因,可以 匿名函数 (闭包) ) set use
继承父作用域中定义的变量,如下所示:
$factor = 10;
$calc = function($num) use($factor){
return $num * $factor;
};
但在 PHP 7.4 中,父作用域的值是隐式捕获的(隐式绑定到值作用域)。所以我们一行就可以完成这个功能。
$factor = 10;
$calc = fn($num) => $num * $factor;
在父作用域中定义的变量可用于箭头函数。相当于我们的useuse
,父级无法更改。 。新语法是对语言的巨大改进,使我们能够构建更具可读性和可维护性的代码。
NULL串联运算符
由于日常使用中存在大量三元表达式和isset()一起使用的情况,我们添加了NULL串联运算符(??)这个语法糖。如果变量存在且不为 NULL,则返回其自己的值,否则返回其第二个操作数。
$username = $_GET['user'] ?? ‘nobody';
这段代码让事情变得非常简单:获取请求参数,如果不存在则设置默认值。但在这个 RFC 示例中,如果我们有更长的变量名怎么办?
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';
此代码很难长期维护。因此,为了帮助开发者写出更直观的代码,这个RFC建议引入null_coalesce_equal_operator ??=
,这样我们就可以输入下面的代码替换上面的代码:
$this->request->data['comments']['user_id'] ??= ‘value’;
如果值为左边参数的值为 null
,使用右边参数的值。
请注意,虽然串联运算符 ??
是比较运算符,但 ??=
是赋值运算符。
类型属性 2.0
定义传递给函数或类的特定类型变量的类型声明、类型提示和方法。其中,类型提示是PHP5中提供的功能。在 PHP 7.2 中,添加了数据类型 object
。 PHP7.4 增加了 主类属性声明 ,参见以下示例:
class User {
public int $id;
public string $name;
}
除了 类型 这样我们就可以放心使用了 “https://wiki.php.net/rfc /nullable_types">允许 nullable Null (?type)void
和 callable 之外 支持所有类型 为什么不呢?你支持
void
和callable
吗?以下是 RFC 的解释void
不支持,因为它不可用且语义不清楚。
因为它没有用且语义不清楚。
类型 callable
不受支持,因为其行为依赖于上下文。bool
、int
、float
, 字符串
, 物体
、可迭代
、self
、父
,当然还有很少使用的
所以在 PHP7.4 中你可以输入这样的代码:
// 静态属性的类型
public static iterable $staticProp;
// var 中声明属性
var bool $flagl
// 设置默认的值
// 注意,只有 nullable 的类型,才能设置默认值为 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中,建议引入类弱引用
。弱引用允许编码保留对对象的引用,但这并不能防止对象被销毁。 ;这对于实现类似缓存的结构很有用。
提案作者示例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,当 object 被销毁的时候,并不会抛出致命错误
NULL
协变回报和逆变参数
协方差和逆变
解释百度百科中的解释
- 不变(Invariant):涵盖了所有需要的types
- 协变:从通用到特定的类型
- 逆变:从特定到通用的类型 目前 PHP 大多有
参数类型 Invariant
,而且大部分都是返回类型Invariant 。这意味着当I是T的参数类型或返回类型时,子类也必须是T的参数类型或返回类型。 。但通常需要处理一些特殊情况,例如特殊返回类型或通用输入类型。提案RFC建议PHP7.4添加协变返回和逆变参数。以下是提案中的示例: 协变回报:
interface Factory {
function make(): object;
}
class UserFactory implements Factory {
// 将比较泛的 object 类型,具体到 User 类型
function make(): User;
}
逆变参数:
interface Concatable {
function concat(Iterator $input);
}
class Collection implements Concatable {
// 将比较具体的 `Iterator`参数类型,逆变成接受所有的 `iterable`类型
function concat(iterable $input) {/* . . . */}
}
预加载
此RFC由 Dmitry Stogov 提出。预加载是在模块初始化时将库和框架加载到OPCache中的过程,如下图
引用他的原话:
在服务器启动时 - 在任何应用程序代码运行之前 - 我们可以加载一组特定的PHP文件存入内存,并使其内容“永久可用”以供该服务器处理的所有后续请求。这些文件中定义的所有函数和类都将立即可供请求使用,就像内部实体一样。
当服务器启动时 - 在我们运行任何应用程序代码之前 - 我们可以将一组 PHP 文件加载到内存中 - 并使其预加载内容对于所有后续请求“永久可用”。这些文件中定义的所有函数和类在请求时都可以立即使用,就像内置函数一样。
预加载由opcache.preload中的php.ini
控制。该参数指定服务器启动时编译并执行的PHP脚本。该文件可用于预加载其他文件或通过函数 opcache_compile_file()
这是一个很大的性能改进,但也有 RFC
预加载文件保持缓存的明显缺点。如果不重新启动服务器,更改各自的源文件将不会产生任何影响。
预加载的文件将永久缓存在 opcache 中。修改相应的源文件时,除非重新启动服务,否则更改不会生效。
新的自定义对象序列化机制
这是由Nikita Popov提出的另一项提案,并获得了绝大多数票数的批准。__sleep()和
$username = $_GET['user'] ?? ‘nobody';
__唤醒 () 魔法方法
可串行化
接口 根据 Nikita 的说法,它们都有潜在的问题:导致代码复杂且不可靠。您可以在 RFC 中深入研究此主题。我只是在这里提到,新的序列化机制应该通过提供两个新的神奇方法 __serialize()
和 __unserialize()
来防止这种情况发生,这两个方法结合了两个现有的问题机制。
该提案以 20 票对 7 票通过。
PHP7.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 建议更改算子的优先级,使 这个提案分为两步: 对 PHP 7.4 alpha 预览版的性能状态感到好奇,我今天对使用 Git Quick Benchmark 构建的 PHP 7.3.6、7.2.18、7.1.29 和 7.0.32 进行了一些测试每个发行版都是以相同的方式构建的。 现阶段,PHPBench 7.4 的性能与稳定的 PHP 7.3 相当,已经比 PHP 7.0 快了约 30%……当然,与过去的 PHP 5.5 相比,收益甚至更大。 以微米为单位,PHP 7.4 的运行速度仅比 PHP 7.3 稍快,而 PHP-8.0 的运行速度大致相同,至少在 JIT 代码稳定并默认启用之前是这样。 在 Phoronix 自己的内部 PHP 基准测试中,PHP 7.4 似乎高于 PHP 7.3 的性能水平 - 至少在这个 pre-alpha 状态下是这样。自 PHP 7.0 以来已经有了一些重大改进,自 PHP5 缓慢发布以来也有许多改进。 总结:PHP7.4是预期的版本,但PHP8是整个PHP世界中最重要的。 .
的优先级低于+
、- 这两个连接字符串之前始终使用运算符执行加法和减法。所以这行代码应该等价于如下:
echo "$sum: " . ($a + $b);
+
-
和。 如果不给予优先执行,将发出放弃通知。
$b = $a == 1 ? ‘一’:$a==2? '二' : $a == 3? '三' : '其他';将被解释为:
$b = (($a == 1 ? '一' : $a == 2) ? '二' : $a == 3 ) ? '三' : '其他';
对于这种复杂的三重表示,它可能不会按照你想要的方式工作,而且很容易导致错误。因此这个RFC建议删除并放弃三元运算符的左结合使用,这要求程序员使用括号。此建议分两步实施: Php7.4 性能
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。