在 PHP 8 中处理数组
除了性能上的改进,PHP 8 在数组处理方面的两个主要变化涉及负偏移量的处理和大括号 ({}
) 的使用。由于这两项变化都可能导致 PHP 8 移植后的应用程序代码中断,因此在这里介绍一下很重要。意识到这里提出的问题,您就有更好的机会在短时间内使损坏的代码恢复正常。
让我们先看看负数组偏移的处理。
处理负偏移量
在 PHP 中为数组赋值时,如果不指定索引,PHP 将自动为您指定一个索引。以这种方式选择的索引是一个整数,它代表的值比当前分配的最高整数键高一。如果尚未分配整数索引键,自动索引分配算法将从零开始。
但在 PHP 7 及以下版本中,这种算法在负整数索引的情况下应用不一致。如果数字数组的索引以负数开始,自动索引就会跳转到零(0),而不管下一个数字通常是多少。而在 PHP 8 中,无论索引是负整数还是正整数,自动索引都会以+1 的值递增。
如果您的代码依赖于自动索引,而任何起始索引都是负数,就可能出现向后兼容的代码中断。由于自动索引是无声发生的,没有任何警告或通知,因此很难检测到这一问题。
下面的代码示例说明了 PHP 7 和 PHP 8 之间的行为差异:
-
首先,我们定义一个只以负整数为索引的数组。我们使用
var_dump()
来显示这个数组:// /repo/ch06/php8_array_negative_index.php $a = [-3 => 'CCC', -2 => 'BBB', -1 => 'AAA']; var_dump($a);
php -
然后,我们定义第二个数组,并将第一个索引初始化为 -3。然后,我们添加额外的数组元素,但不指定索引。这样就会产生自动索引:
$b[-3] = 'CCC'; $b[] = 'BBB'; $b[] = 'AAA'; var_dump($b);
php -
如果我们在 PHP 7 中运行该程序,会发现第一个数组的渲染结果是正确的。在 PHP 7 和更早的版本中,只要是直接赋值,数组索引完全可以是负数。下面是输出结果:
root@php8_tips_php7 [ /repo/ch06 ]# php php8_array_negative_index.php /repo/ch06/php8_array_negative_index.php:6: array(3) { [-3] => string(3) "CCC" [-2] => string(3) "BBB" [-1] => string(3) "AAA" } /repo/ch06/php8_array_negative_index.php:12: array(3) { [-3] => string(3) "CCC" [0] => string(3) "BBB" [1] => string(3) "AAA" }
bash -
但是,从
var_dump()
的第二个输出中可以看到,自动数组索引会跳到零,而不管之前的高值是多少。 -
而在 PHP 8 中,输出结果是一致的。下面是 PHP 8 的输出结果:
root@php8_tips_php8 [ /repo/ch06 ]# php php8_array_negative_index.php array(3) { [-3]=> string(3) "CCC" [-2]=> string(3) "BBB" [-1]=> string(3) "AAA" } array(3) { [-3]=> string(3) "CCC" [-2]=> string(3) "BBB" [-1]=> string(3) "AAA" }
bash -
从输出中可以看到,数组索引已自动分配,并以 1 为增量,使两个数组完全相同。
有关该增强功能的更多信息,请参阅本文: https://wiki.php.net/rfc/negative_array_index 。
现在,您已经了解了涉及负值的索引自动分配的潜在代码漏洞,让我们把注意力转向另一个值得关注的方面:大括号的使用。
处理花括号使用变化
对于创建 PHP 代码的开发人员来说,大括号 ({}
) 是再熟悉不过的了。PHP 语言是用 C 语言编写的,其中大量使用了 C 语言的语法,包括大括号。众所周知,大括号用于在控制结构(例如 if {}
)、循环(例如 for () {}
)、函数(例如 function xyz() {}
)和类中划分代码块。
不过,在本小节中,我们将只讨论与变量相关的大括号用法。PHP 8 中一个潜在的重大变化是使用大括号来标识数组元素。从 PHP 8 开始,使用大括号指定数组偏移量的做法已被弃用。
由于以下原因,旧的用法一直备受争议:
-
它的使用很容易与双引号字符串内的大括号混淆。
-
大括号不能用于数组赋值。因此,PHP 核心团队需要使大括号的使用与方括号(
[ ]
)…一致或者干脆取消大括号的使用。最后的决定是取消对数组的大括号支持。
有关此更改背后背景的更多信息,请参阅以下链接: https://wiki.php.net/rfc/deprecate_curly_braces_array_access 。 |
下面的代码示例可以说明这一点:
-
首先,我们定义了一个回调数组,以说明删除或非法使用大括号的情况:
// /repo/ch06/php7_curly_brace_usage.php $func = [ 1 => function () { $a = ['A' => 111, 'B' => 222, 'C' => 333]; echo 'WORKS: ' . $a{'C'} . "\n";}, 2 => function () { eval('$a = {"A","B","C"};'); }, 3 => function () { eval('$a = ["A","B"]; $a{} = "C";'); } ];
php -
然后,我们使用
try/catch
块对回调进行循环,以捕捉抛出的错误:foreach ($func as $example => $callback) { try { echo "\nTesting Example $example\n"; $callback(); } catch (Throwable $t) { echo $t->getMessage() . "\n"; } }
php
如果我们在 PHP 7 中运行该示例,第一个回调可以正常工作。第二个和第三个回调会导致抛出 ParseError
:
root@php8_tips_php7 [ /repo/ch06 ]#
php php7_curly_brace_usage.php
Testing Example 1
WORKS: 333
Testing Example 2
syntax error, unexpected '{'
Testing Example 3
syntax error, unexpected '}'
bash
然而,当我们在 PHP 8 中运行同一示例时,所有示例都无法运行。下面是 PHP 8 的输出结果:
root@php8_tips_php8 [ /repo/ch06 ]#
php php7_curly_brace_usage.php
PHP Fatal error: Array and string offset access syntax with
curly braces is no longer supported in /repo/ch06/php7_curly_
brace_usage.php on line 8
bash
这种潜在的代码中断很容易检测到。不过,由于您的代码中有很多大括号,您可能需要等待抛出致命错误(Fatal Error)才能捕捉到代码中断。
现在您对 PHP 8 中数组处理的变化有了一定的了解,让我们来看看安全相关函数的变化。