有用 PHP 的大佬帮我解释下这个诡异的结果

2020-03-06 15:07:27 +08:00
 zjsxwc

今天测试反馈了个异常行为,具体是买多个商品最后会漏掉最后一个商品

php 版本 7.0,简化后代码如下:


$itemIdAndQuantityListJson = '[{"itemId":11215,"quantity":10},{"itemId":10234,"quantity":1},{"itemId":10444,"quantity":1},{"itemId":10447,"quantity":1},{"itemId":10448,"quantity":1},{"itemId":10449,"quantity":1}]';
/** @var array $itemIdAndQuantityList */
$itemIdAndQuantityList = json_decode($itemIdAndQuantityListJson, true);

try {
    /** @var int[] $itemIdList */
    $itemIdList = [];
    foreach ($itemIdAndQuantityList as &$itemIdAndQuantity) {
        $itemIdAndQuantity["itemId"] = intval($itemIdAndQuantity["itemId"]);
        if (!$itemIdAndQuantity["itemId"]) {
            throw new \RuntimeException("Parameters fault",15);
        }
        if (in_array($itemIdAndQuantity["itemId"], $itemIdList)) {
            throw new \RuntimeException("Parameters fault",16);
        }
        $itemIdList[] = $itemIdAndQuantity["itemId"];
    }
} catch (\Throwable $e) {
    var_dump($e);
    die;
}

/** @var int[] $itemIdList */
$itemIdList = [];


//重现期望结果的开关
$showExpect = false;

if ($showExpect) {
    //期望的正常结果 返回不同的数字
    foreach ($itemIdAndQuantityList as &$itemIdAndQuantity) {
        $itemIdList[] = $itemIdAndQuantity["itemId"];
    }
} else {
    //出现奇怪的结果 返回最后两个 数字变相同了
    foreach ($itemIdAndQuantityList as $itemIdAndQuantity) {
        $itemIdList[] = $itemIdAndQuantity["itemId"];
    }
}

var_dump($itemIdList);
2436 次点击
所在节点    PHP
8 条回复
zjsxwc
2020-03-06 15:21:55 +08:00
补充:
期望结果($showExpect = true;)打印
```
[4]=>
int(10448)
[5]=>
int(10449)
```


诡异结果($showExpect = false;)打印
```
[4]=>
int(10448)
[5]=>
int(10448)
```
zjsxwc
2020-03-06 15:35:47 +08:00
猜到原因了,

foreach 第一次的时候会 对`$itemIdAndQuantity`赋值, 此时`$itemIdAndQuantity`仍旧是对最后一个元素的引用,于是最后一个元素被覆盖成第一个值了,赋值之后`$itemIdAndQuantity`就不在是个引用了,执行正常逻辑。

解决办法就是要么全用 `&$itemIdAndQuantity` ,要么在每次 foreach 之后 unset 掉那个引用。
z5864703
2020-03-06 15:43:22 +08:00
你可以在$showExpect = false 之前 dump 下$itemIdAndQuantityList 的值就知道了。
最后一个键是引用值
Airon
2020-03-06 15:54:43 +08:00
引用的问题,要么都用&,要么 unset 掉,要么用不一样的命名
skymei
2020-03-06 16:13:52 +08:00
```PHP
$data = [1, 2, 3, 4];
$datum = &$data[3];
foreach ($data as $datum) {
xdebug_debug_zval('data');
}
skymei
2020-03-06 16:41:50 +08:00
```PHP
$data = [1, 2, 3, 4];
$datum = &$data[3];
foreach ($data as $datum) {
xdebug_debug_zval('data');
}
```
代码简化如上:
1.$datum 指向数组的最后一个元素地址和值 4
2.$datum 被赋予数组第一个元素值,由于是引用,因此将数组的第四个元素值改为了第一个元素值 1, 地址引用仍指向最后一个元素 [1, 2, 3, 1]
3.$datum 被赋予数组第二个元素值,由于是引用,因此将数组的第四个元素值改为了第二个元素值 2, 地址引用仍指向最后一个元素 [1, 2, 3 ,2]
4.$datum 被赋予数组第三个元素值,由于是引用,因此将数组的第四个元素值改为了第三个元素值 3, 地址引用仍指向最后一个元素 [1, 2, 3 ,3]
4.$datum 被赋予数组第四个元素值,由于是引用,因此将数组的第四个元素值改为了第四个元素值 3, 地址引用仍指向最后一个元素 [1, 2, 3 ,3]
Achiii
2020-03-06 16:45:55 +08:00
我猜你用了引用,引用之后第二个循环会丢掉最后一个元素。别问我怎么知道的
pinews
2020-03-06 19:15:58 +08:00
建议新建一个数组。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/650415

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX