PHP - foreach 使用空合并运算符丢失引用
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);
dos ??
复制一份?如果 $a
为空并且 foreach
将失败,我只是不想用 if (isset($a))
包装 foreach
。
您可以使用扩展数组语法获取索引,然后使用它来取消引用原始数组值:
$a = [1, 2];
foreach ($a ?? [] as $i => $v) {
++$a[$i];
}
var_dump($a);
但请注意,无论如何这可能都没用,因为如果未设置 $a
(以便 ??
符合条件),则循环将进行零次迭代并且 $a
仍将为 var_dump()
取消设置。 (除非那是你需要的,我想......)
I just dont want to wrap the foreach
with a if (isset($a))
if $a
is null and foreach
will fail.
如果您没有将变量初始化为正确的类型,这是不可避免的,但是在使用按引用传递、return 按引用和默认值的实用函数中有一些技巧:
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 ?? []
returns $a
的值或[1, 2]
。因此 $a
不会被使用 &$v
.
对这个值的值的引用修改
对象是通过引用分配的,因此在这种情况下引用被 returned 并且原始对象被修改,除非 $a
未设置。那么你显然会得到:
Notice: Undefined variable: a
$a = (object)[1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
这产生:
object(stdClass)#1 (2) {
["0"]=>
int(2)
["1"]=>
&int(3)
}
Assignment by reference is also supported, using the "$var = &$othervar;
" syntax. Assignment by reference means that both variables end up pointing at the same data, and nothing is copied anywhere.
在这些情况下,它们都是表达式,无法引用:
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);
如果这只是一个很好的练习,但如果您要尝试解决特定问题,那么您需要概述更广泛的问题。
TL;DR 对于您的情况,您可以考虑以这种方式使用空合并运算符:
$a = $a ?? [];
foreach ($a as &$v) { ... }
或者,根本不使用引用,方法是使用 array_map()
或使用键在基础数组中进行修改。
Q1
$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 ~3, ->8
... rest of looping code
FE_RESET_RW
的第一个操作数是将被迭代的散列变量,你可以看到它是~3
而不是!0
(你的$a
代码),这正是您所期望的。
Q2
foreach ($a = [1, 2] as &$v) {
$v++;
}
这里发生的是赋值 $a = [1, 2]
的 return 值被用作要迭代的数组。
您可以将此行为与以下内容进行比较:
$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 !0, <array>
1 > FE_RESET_RW , ->6
... rest of looping code
同样,</code> 是 <code>FE_RESET_RW
的第一个操作数,它是赋值结果,因此不会对 !0
进行迭代(代码中的 $a
).
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);
dos ??
复制一份?如果 $a
为空并且 foreach
将失败,我只是不想用 if (isset($a))
包装 foreach
。
您可以使用扩展数组语法获取索引,然后使用它来取消引用原始数组值:
$a = [1, 2];
foreach ($a ?? [] as $i => $v) {
++$a[$i];
}
var_dump($a);
但请注意,无论如何这可能都没用,因为如果未设置 $a
(以便 ??
符合条件),则循环将进行零次迭代并且 $a
仍将为 var_dump()
取消设置。 (除非那是你需要的,我想......)
I just dont want to wrap the
foreach
with aif (isset($a))
if$a
is null andforeach
will fail.
如果您没有将变量初始化为正确的类型,这是不可避免的,但是在使用按引用传递、return 按引用和默认值的实用函数中有一些技巧:
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 ?? []
returns $a
的值或[1, 2]
。因此 $a
不会被使用 &$v
.
对象是通过引用分配的,因此在这种情况下引用被 returned 并且原始对象被修改,除非 $a
未设置。那么你显然会得到:
Notice: Undefined variable: a
$a = (object)[1, 2];
foreach ($a ?? [] as &$v) {
$v++;
}
var_dump($a);
这产生:
object(stdClass)#1 (2) {
["0"]=>
int(2)
["1"]=>
&int(3)
}
Assignment by reference is also supported, using the
"$var = &$othervar;
" syntax. Assignment by reference means that both variables end up pointing at the same data, and nothing is copied anywhere.
在这些情况下,它们都是表达式,无法引用:
$c = &($a);
$c = &($a ?? []);
$c = &($a + 1);
如果这只是一个很好的练习,但如果您要尝试解决特定问题,那么您需要概述更广泛的问题。
TL;DR 对于您的情况,您可以考虑以这种方式使用空合并运算符:
$a = $a ?? [];
foreach ($a as &$v) { ... }
或者,根本不使用引用,方法是使用 array_map()
或使用键在基础数组中进行修改。
Q1
$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 ~3, ->8
... rest of looping code
FE_RESET_RW
的第一个操作数是将被迭代的散列变量,你可以看到它是~3
而不是!0
(你的$a
代码),这正是您所期望的。
Q2
foreach ($a = [1, 2] as &$v) {
$v++;
}
这里发生的是赋值 $a = [1, 2]
的 return 值被用作要迭代的数组。
您可以将此行为与以下内容进行比较:
$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 !0, <array>
1 > FE_RESET_RW , ->6
... rest of looping code
同样,</code> 是 <code>FE_RESET_RW
的第一个操作数,它是赋值结果,因此不会对 !0
进行迭代(代码中的 $a
).