[英]PHP - foreach lose reference with null coalescing operator
Q1:我认为??
在以下情况下什么都不做:
$a = [1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
但为什么?
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
Q2:这更奇怪:
foreach ($a = [1, 2] as &$v) {
$v++;
}
var_dump($a);
// output
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
我的想法:我认为这些表达式是不可引用的,但是foreach
捕获错误或以某种方式然后复制。 工作参考:
$a = 1;
$c = &$a;
不工作:
$a = 1;
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);
??
复印一份? 如果$a
为空并且foreach
将失败,我只是不想用if (isset($a))
包装foreach
。
TL;DR对于您的情况,您可以考虑以这种方式使用空合并运算符:
$a = $a ?? [];
foreach ($a as &$v) { ... }
或者,根本不使用引用,通过使用array_map()
或使用键在底层数组中进行修改。
$a = [1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
合并运算符使用原始数组的副本,然后在为null
应用右手操作数。 因此,迭代发生在原始数组的副本上。
您可以将其与以下内容进行比较:
$a = [1, 2];
$x = $a ?? [];
$x[1] = 4;
var_dump($a); // [1, 2]
compiled vars: !0 = $a, !1 = $v
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
8 0 E > ASSIGN !0, <array>
9 1 COALESCE ~3 !0
2 QM_ASSIGN ~3 <array>
3 > FE_RESET_RW $4 ~3, ->8
... rest of looping code
FE_RESET_RW
的第一个操作数是将被迭代的哈希变量,您可以看到它是~3
而不是!0
(代码中的$a
),这是您期望发生的。
foreach ($a = [1, 2] as &$v) {
$v++;
}
这里发生的是赋值$a = [1, 2]
的返回值被用作迭代数组。
您可以将此行为与以下内容进行比较:
$x = $a = [1, 2];
$x[0] = 4; // modify in-place
var_dump($a); // [1, 2]
compiled vars: !0 = $a, !1 = $v
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > ASSIGN $2 !0, <array>
1 > FE_RESET_RW $3 $2, ->6
... rest of looping code
同样, $2
是FE_RESET_RW
的第一个操作数,它是赋值结果,因此迭代不会发生在!0
(代码中的$a
)上。
您可以使用扩展数组语法来获取索引,然后使用它来取消引用原始数组值:
$a = [1, 2];
foreach ($a ?? [] as $i => $v) {
++$a[$i];
}
var_dump($a);
但请注意,无论如何这可能是无用的,因为如果$a
未设置(以便??
合格),则循环将进行零次迭代,并且$a
仍然不会为var_dump()
。 (除非那是你需要的,我想......)
如果
$a
为空并且foreach
将失败,我只是不想用if (isset($a))
包装foreach
。
如果您没有将变量初始化为正确的类型,这是不可避免的,但这里有一些使用按引用传递、按引用返回和默认值的实用函数中的技巧:
function &test(&$array=[]) {
return $array;
}
$a = [1, 2];
foreach (test($a) as &$v) {
$v++;
}
如果$a
未设置且不循环,则不会产生错误,但是在上面它会产生:
array(2) {
[0]=>
int(2)
[1]=>
&int(3)
}
在 PHP 中数组是按值赋值的(赋值副本),所以如果$a !== null
那么$a ?? []
$a ?? []
返回$a
或[1, 2]
。 所以$a
不会被使用&$v
引用这个值的值所修改。
对象是通过引用分配的,因此在这种情况下会返回一个引用并修改原始对象,除非未设置$a
。 那么你显然会得到:
注意:未定义变量:a
$a = (object)[1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
这产生:
object(stdClass)#1 (2) {
["0"]=>
int(2)
["1"]=>
&int(3)
}
从参考分配:
也支持通过引用赋值,使用
"$var = &$othervar;
”语法。 通过引用赋值意味着两个变量最终都指向相同的数据,并且不会在任何地方复制任何内容。
在这些情况下,它们都是表达式,不能被引用:
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);
如果这只是一个练习,那很好,但如果你试图解决一个特定的问题,那么你需要概述这个更广泛的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.