PHPのfgetcsvやfputcsvでファイルを扱う時の排他処理(その3)
前回の続き
fputcsv、fwriteなどは大抵失敗したときにfalseを返すので
以下のようにfputcsvの度にfalse(失敗)を取得するようにする。
んでもって失敗した場合の処理をどうにかする。
以下ソース(何気に前回とコーディングスタイルが違うが気にしない)
<?php // 省略 $success = false; if (file_lock($file) { $res = true; $fp = @fopen($file ,'w'); if ($fp) { foreach ($data as $row) { $res = $res && @fputcsv($fp, $row); } fclose($fp); } else { $res = false; } file_unlock($file); } if (! $res) { // エラー処理 } // 省略 ?>
...このままだと失敗した場合を検知するけど、
fputcsvのループ最中で失敗してるとファイルが中途半端になって壊れてしまう。
あとでエラー処理をしてもすでに遅いじゃないか。
回避するのに思いついた方法は...
・「元ファイル名.tmp」で仮ファイルを作成
・仮ファイルにデータを書き込み
・エラーが無く終了していたら元ファイルを削除、仮ファイルを元ファイル名にリネーム
・エラーがあれば仮ファイルを削除
という方法
以下ソース
<?php // 省略 $success = false; if (file_lock($file) { $res = true; $tmp_file = $file . '.tmp'; $fp = @fopen($tmp_file ,'w'); if ($fp) { foreach ($data as $row) { $res = $res && @fputcsv($fp, $row); } fclose($fp); if ($res) { @unlink( $file ); @rename( $tmp_file, $file ); } else { @unlink( $tmp_file ); } } else { $res = false; } file_unlock($file); } if (! $res) { echo 'なんか失敗っぽいですよ'; } // 省略 ?>
...しかし
元ファイル削除やリネームで失敗したらどうなるんだろ...?
そもそも失敗するのか?
いやありえるか、世の中に絶対なんて無い!
失敗したらどうなるか?
元ファイルの削除失敗ならデータが更新されないだけなので大した問題では無い気がする。
問題はリネーム失敗の場合、仮ファイルだけが残り、元ファイルが無いまま....
だったらrenameを数回リトライする
何回?100回リトライしても駄目だったら?
駄目だったら諦める?
...いや、そこまで考えるなら最初っからDBを使うべきか。
またはアプリ側でバックアップ機能を付けるとかの方がいいか。
うむぅ。