記兩次迴圈語句中陣列、變數未宣告(未銷燬)導致的bug
上個月稀裡糊塗的犯了兩次差不多的問題.....特記下,以示警醒。
第一次:悲催的改了三次程式碼...
根據特定條件,列印curl語句,然後放到 .sh檔案中,用Shell來執行。
查詢表***,根據產品id和結束時間(大於當前時間)查詢使用者。通過介面執行特定語句(就是我打印出來的語句)。
第一次程式碼:
$config = array( array(1,2,3,4), array(5,6,7,8) ); foreach ($config as $item) { $sql = "select * from (select userid,enddate from tablename where id in (?,?,?) and enddate > now() order by enddate desc) as a group by userid"; $sth = DB_Pdo::instance()->get_dbh()->prepare($sql); $sth->execute(array($item[0],$item[1],$item[1])); $arr = $sth->fetchAll(PDO::FETCH_ASSOC)) $sth = null; foreach ($arr as $value) { echo "curl '連結(此處省略)&sid=".$item[3]."&enddate=".$value['enddate']."&userid=".$value['userid']."'\n"; } }
這個程式碼在程式碼層面是沒問題的,但是當資料量比較大的時候就會出現問題了,巢狀的sql語句執行起來很耗費時間、資源,可能會導致宕機。因此廢棄,換一種方式。 ......... 悲劇即將到來。
第二次程式碼:
$config = array( array(1,2,3,4), array(5,6,7,8) ); $userid_arr = array(); foreach ($config as $item) { $sql = "select userid,enddate from tablename where id in (?,?,?) and enddate > now() order by id"; $sth = DB_Pdo::instance()->get_dbh()->prepare($sql); $sth->execute(array($item[0],$item[1],$item[1])); while (false !== $row = $sth->fetch(PDO::FETCH_ASSOC)) { if($userid_arr[$row['userid']] < $row['enddate']){ $userid_arr[$row['userid']] = $row['enddate']; } } $sth = null; foreach ($userid_arr as $key => $value) { echo "curl '連結(此處省略)&sid=".$item[3]."&enddate=".$value."&userid=".$key."'\n"; } }
上邊程式碼,改了sql語句,但是用到了迴圈。。。大眼一看,好像沒問題,但是當執行的時候就會發現,curl語句比第一次程式碼執行出來的要多一部分。然後才發現迴圈語句中的 $userid_arr 是在foreach外面宣告的,也就是說foreach迴圈第二次的時候$userid_arr陣列內依然包含第一次迴圈後的結果,此時並不是空陣列......然後第二次foreach的時候會把第一次迴圈過的資料再次迴圈。。。
所以又改了第三次,將$userid_arr = array(); 移到while上面,這樣每次foreach的時候都會執行 userid_arr = array()
做迴圈的時候,遇到陣列要麼在之前宣告,要麼就在底部銷燬。。。切記切記。。。然鵝。峩還是莫記得......不過這次是變數,不是陣列了.........
第二次犯的錯誤和第一次類似
指令碼程式碼大體上就是這樣(程式碼過長,就縮略一下)
while(1){
$result = 從佇列取出來的資料;
if(! $result){
echo "*";
sleep(1);
}elseif($result){
處理 $result 資料;
*
*
*
*
*一大堆程式碼(省略)···
if(isset($result['appflag']) && isset($result['appid'])){
$appflag = $result['appflag'];
$appid = $result['appid'];
}
*
*
*
*
*又一大堆程式碼(省略)···
if($appflag && $appid){
危險程式碼........
}
}
}
$result 是從kafka取出來的陣列,但是資料還不一樣,有些包含了 $result['appflag'] 和 $result['appid']。。 有些陣列則沒有這兩項。這就是噩夢的開端,執行了之後,發現。。。唉~~~ 沒有包含$result['appflag'] 和 $result['appid']的資料,也執行了“危險程式碼”。仔細檢查發現:if($appflag && $appid) 並未生效,看了下發現是while迴圈,當運行了一次包含了 $result['appflag'] 和 $result['appid']的資料後,$appflag 、 $appid 這兩個變數就一直存在了.....再迴圈的時候 $appflag && $appid 就一直為true了,程式碼也就一直執行了。
程式碼長並不是未宣告$appflag 和 $appid 的藉口,幸好及時發現,在執行完“危險程式碼”後,增加銷燬$appflag 、 $appid 兩個變數的程式碼,直接unset掉。。。或者程式碼中一直使用$result['appflag'],$result['appid'] 不做賦值操作,也不會出現這種問題。
總之總之,迴圈操作一定要倍加小心,用到陣列要宣告,用到迴圈內用到判斷一定要仔細看下條件是否需要做處理。。。
切記,以上。