昨日気づいたことなんですが、以下のような警告文がずらりと出てしまう事態が発生しました。



■警告文現る
<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);

$file= "data.txt";
$fp= fopen($file, "rb");
while (!feof($fp)){


    list($a,$b,$c,$d,$e,$f)= explode('<>', rtrim(fgets($fp)), 6);
}
fclose($fp);


?>



[出力結果]
Notice: Undefined offset: 5 in C:\xampplite\htdocs\index.php on line 22

Notice: Undefined offset: 4 in C:\xampplite\htdocs\index.php on line 22


Notice: Undefined offset: 3 in C:\xampplite\htdocs\index.php on line 22


Notice: Undefined offset: 2 in C:\xampplite\htdocs\index.php on line 22


Notice: Undefined offset: 1 in C:\xampplite\htdocs\index.php on line 22



error_reporting()関数を使ってエラー警告レベルを上げたところ上記のような警告文がずらりと表示されるようになりました。




■警告源の特定

<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);

$file= "data.txt";
$fp= fopen($file, "rb");
while (!feof($fp)){


    $tmp= fgets($fp);
    echo strlen($tmp),"<br />\n";
// list($a,$b,$c,$d,$e,$f)= explode('<>', rtrim($tmp), 6);
}
fclose($fp);


?>



[出力結果]

211
222
212
200
151
229
168
171
181
118
159
158
136
0


警告が出た辺りをいろいろとコメントにしてやっと特定できたのがexplode()で6分割してる行でした。


「Notice: Undefined offset: 5」~「Notice: Undefined offset: 1」までの警告文は、6分割したうちの5番目~1番目までは未定義値、つまり値が最後の6番目だけにしかセットできなかったという内容のようですね。


fgets()で読み出す部分をくくり出して、取得したデータ長を順に表示させてみると必ず最後に 0長データを取得してることがわかりました。
別のファイルでやってみてもfopen("~", "r")とかfgets($fp, 1024)とかいろいろ変えても常に最後に0長データを読み出してるということがわかりました。
つまり(!feof($fp))のループ条件を満たしてるのに最後に0長データを読み出してることになります。
これは環境特有の現象なのかな?



■対策と結論

<?php

error_reporting(E_ALL);
ini_set("display_errors", 1);


$file= "data.txt";
$fp= fopen($file, "rb");
while (!feof($fp)){


    if (($tmp= fgets($fp)) === false) break;
    echo strlen($tmp),"<br />\n";
}
fclose($fp);


?>



[出力結果]

211
222
212
200
151
229
168
171
181
118
159
158
136


というわけでfgets()で読み取るデータは一旦は一時変数に読み込んでfalseが返ってきてるかどうかチェックするようにしたほうが手堅いゾと



実質機能していないfeof()を使わずにエラーハンドリングを丁寧に書くと

<?php

$file= "data.txt";
if (file_exists($file) and $fp= fopen($file, "rb")){
    while (($tmp=fgets($fp)) !== false){
        echo strlen($tmp),"<br />\n";
    }

    fclose($fp);
}

?>