文章大纲

我怎么又给自己创造了一个bug系列(一):疏忽使用多个foreach所带来的灾难

2023年04月08日 13:41

1.用户发现了一个bug

很幸运,我又给自己创造了一个bug。


就是原本一直正常的功能,突然被用户4月6号发现了一个bug:

设备一直全部都是运行状态,有一台设备却调皮地“灰“头土脸了。



但奇妙的是,我压根儿就不知道自己是怎么创造出这个bug的。


费了一番功夫,最终找出问题出在下面这段代码上:



代码中的问题,我将在文末揭晓。如果你有兴趣找bug的话,可以先瞅瞅。


本文是翟码农个人博客PHP分类下的原创文章,转载请注明出处:http://www.zhai14.com/blog/why-I-made-a-bug-again-1.html,我怎么又给自己创造了一个bug系列(一)


2.bug如何产生的?

不过本文的重点,我不是要跟大家分享我怎么找到bug的,而是和大家分享:

我是怎么不知不觉给自己创造了bug?


经过日志追溯,发现2023年3月3号本人改动了上面那段代码。改动之前的代码如下所示:

//批量获取设备当前数据
public function getMultiDeviceCurrentInfo($arrDeviceId){
    $model = new EquipmentMongo();
    return $model->getCurrentInfoByDeviceIds($arrDeviceId);
}

//批量获取设备当前状态
public function getMultiDeviceCurrentStatus($arrDeviceId)
{
    $deviceInfo = $this->getMultiDeviceCurrentInfo($arrDeviceId);
    $newList = [];
    foreach($deviceInfo as $item){
        if(isset($item['status']) && false === strpos($item['device_id'], 'GTRY_')){
            //电表采集status暂时有误,用connection_status
            $status = $item['status'];
        }else{
            $status = $item['connection_status'];
        }
        $statusInfo = $this->getRunStatus($status);   //电这块没有status属性数据
        $newList[] = array(
            'device_id' => $item['device_id'],
            'status_run_text' => $statusInfo['status_run_text'],
            'status_run' => $statusInfo['status_run']
        );
    }
    //模拟数据
    foreach($arrDeviceId as $deviceId){
        if( in_array($deviceId, array('SH-PD-01', 'SH-PD2-01', '729-11SY', 'SH-PD-099'))){
            $item = [];
            $item['device_id'] = $deviceId;
            $item['status_run_text'] = 'run';
            $item['status_run'] = 1;
            $newList[] = $item;
        }
    }
    return array_column($newList, 'status_run_text', 'device_id');
}


改动之后如下所示:

//批量获取设备当前数据
public function getMultiDeviceCurrentInfo($arrDeviceId){
    $model = new EquipmentMongo();
    $list = $model->getCurrentInfoByDeviceIds($arrDeviceId);
    $currentTime = strtotime(DateUtil::getCurrentDatetime());
    foreach($list as &$item){
        if(isset($item['status']) && false === strpos($item['device_id'], 'GTRY_')){
            if($currentTime - strtotime($item['time']) > 300){
                //超过5分钟没更新数据,暂认为数据采集中断
                $status = "interrupt";
            }else{
                $status = $item['status'];
            }
        }else{
            //电表采集status暂时有误,用connection_status
            $status = $item['connection_status'];
        }
        $statusInfo = $this->getRunStatus($status);
        $item['status_run_text'] = $statusInfo['status_run_text'];
        $item['status_run'] = $statusInfo['status_run'];
    }
    //模拟数据
    foreach($arrDeviceId as $deviceId){
        if( in_array($deviceId, array('SH-PD-01', 'SH-PD2-01', '729-11SY', 'SH-PD-099'))){
            $item = [];
            $item['device_id'] = $deviceId;
            $item['status_run_text'] = 'run';
            $item['status_run'] = 1;
            $list[] = $item;
        }
    }
    return $list;
}

//批量获取设备当前状态
public function getMultiDeviceCurrentStatus($arrDeviceId)
{
    $deviceInfo = $this->getMultiDeviceCurrentInfo($arrDeviceId);
    return array_column($deviceInfo, 'status_run_text', 'device_id');
}


可以看出,正是我将设备状态status_run这个数据,转移到getMultiDeviceCurrentInfo这个方法里,然后一不小心,就不知不觉给自己埋藏了bug。


我的bug就是:

第二个foreach中重置$item变量,会将第一个foreach中最后一个$item元素给重置为空,因为恰巧$item用了引用,所以最终$list里就少了一台设备数据

这也就是为何用户从界面上发现有台设备状态为离线(程序给了默认值,如果不是运行状态,就默认为离线)。


3.教训总结

本人貌似犯了好几次这种白白耽误自己功夫的错误,今天特记之,希望日后能多加注意:

写多个foreach循环遍历时,尽量用不同命名的$item变量

除非你特意需要这么做,例如:我要给自己的明天任务留一个bug。




  • 2023年04月08日 12:24文章创建
  • 2023年04月08日 13:41文章发布
我要评论
评论列表
暂无评论,期待你的评论哦!
回到顶部