V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zjsxwc
V2EX  ›  PHP

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

  •  
  •   zjsxwc · 2020-03-06 15:07:27 +08:00 · 2419 次点击
    这是一个创建于 1724 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    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);
    
    zjsxwc
        1
    zjsxwc  
    OP
       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
        2
    zjsxwc  
    OP
       2020-03-06 15:35:47 +08:00
    猜到原因了,

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

    解决办法就是要么全用 `&$itemIdAndQuantity` ,要么在每次 foreach 之后 unset 掉那个引用。
    z5864703
        3
    z5864703  
       2020-03-06 15:43:22 +08:00
    你可以在$showExpect = false 之前 dump 下$itemIdAndQuantityList 的值就知道了。
    最后一个键是引用值
    Airon
        4
    Airon  
       2020-03-06 15:54:43 +08:00
    引用的问题,要么都用&,要么 unset 掉,要么用不一样的命名
    skymei
        5
    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
        6
    skymei  
       2020-03-06 16:41:50 +08:00   ❤️ 1
    ```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
        7
    Achiii  
       2020-03-06 16:45:55 +08:00
    我猜你用了引用,引用之后第二个循环会丢掉最后一个元素。别问我怎么知道的
    pinews
        8
    pinews  
       2020-03-06 19:15:58 +08:00
    建议新建一个数组。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1646 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:38 · PVG 00:38 · LAX 08:38 · JFK 11:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.