Blogブログ

PHPで安全にデータをファイルに書き込む方法

2012/11/09

アクセスカウンターを作成した場合PHPでは

通常だとこんな感じでファイルに書き込むとおもいます。

//データ読み込み&作成
$data = file_get_contents ( $file_path );
$data = intval ( $data ) + 1;

//データ書き込み
$fp = @fopen ( $file_path , "w" );
flock ( $fp , LOCK_EX );
fwrite ( $fp , $data );
flock ( $fp , LOCK_UN );
fclose ( $fp );

WEBアプリケーションのアクセスカウンターなどでこのような感じでファイル操作をしていると

ファイルの中身がクラッシュする原因になります。

fopen の第2引数に w の説明をリファレンスで確認すると

「書き出しのみでオープンします。ファイルポインタをファイルの先頭に置き、

ファイルサイズをゼロにします。ファイルが存在しない場合には、 作成を試みます。」とあります。

これはどうゆう事かというと、6行目でファイルサイズをゼロ、すなわちファイルのデータが空になるということです。

そして、その後7行目でファイルをロックしています。ファイルの中身を空にした後にロックしても遅いということです。

例えば2行目の$dataの中身が10だったとします。

Aさんがウェブページにアクセスし6行目の処理が終わったタイミングでBさんがウェブページにアクセスします。

この時Aさんの$dataの中身は3行目に1足されているので11。Bさんの$dataの中身は0です。

そして、Aさんが6?10行目の処理をしてファイルにデータを書き込みを完了します。

この時の$dataの中身は11です。

ですが、Bさんが保持しているデータはAさんが3行目の処理が終わったタイミングだったのでファイルが空($dataが0)の状態です。

そこにBさんが2?10行目の処理を行うと$dataの中身は1としてファイルに保存されてしまい、期待しているように

アクセスカウンターの数字が伸びていきません。

この現象は本当に一瞬しか起きないのでなかなか遭遇しませんが、実際私は以前にこの現象をくらいファイルの中身が空になってしまった事があります。

ではどうすればいいか・・・。

下記のような関数を作っておいてあげればいつでも汎用的に安全にファイルにデータを書き込む事ができます。

function f_write ( $data , $file_path ) {
   //ファイルがなければ作成
   if ( $fp = @fopen ( $file_path , "x" ) ) {
      fclose ( $fp );
   }
   $fp = fopen ( $file_path , "rb+" ); //読み込み/書き出し用にオープン

   if ( flock ( $fp , LOCK_EX ) ) { //排他的ロック
      ftruncate($fp, 0); //ファイルの中身を削除
      fwrite ( $fp , $data );//ファイルにデータを書き込む
   }else{
      return false;//ファイルロックが出来なかった場合。
   }
   return fclose ( $fp ); //ここでロックが解除される。
}

ポイントは

・3行目でファイルが存在しない場合はファイルの作成をします。
・ファイルポインタを取得するときにファイルを空にしない。
・ロックしたあとにftruncate関数でファイルの中身を空にする。
・ファイルの中身を空にした後に書き込みをする

という流れです。

今回はアクセスカウンターを例にしましたが、

掲示板やその他ファイルに書き込む場合は全てに言えることなのでご参考になれば。

他社より断然お得で使いやすい
”アクトのWEB制作”
ACTホームページ相談窓口 / 
システム開発相談窓口