【php】rename関数にご用心
重箱の隅っこの話ですが、こんなことがありましたのメモ。
PHPの関数に、ファイルの最終更新日時を取得する「filemtime」という関数がある。
一連の処理中に一度でもfilemtimeすると、結果がキャッシュされる(マニュアルの注意に書いてあるとおり)。
ファイルの更新日時を参照してから、ファイルを書き換えて、また同じファイルの更新日時を参照すると、古い情報が返ってくる。
では、ファイルの更新日時を参照してから、ファイル名を削除して、また同じファイルの更新日時を参照すると?
→ unlink関数で削除した場合は、キャッシュはクリアしてもらえるそうなので問題なし(マニュアル)。
では、ファイルの更新日時を参照してから、ファイル名を変更して、また同じファイルの更新日時を参照すると?
→ rename関数でファイル名を変更したところ、
- php4ではキャッシュが効いていた。存在しないファイルの更新日が返ってきた。
- php5ではキャッシュがクリアされるらしく、新しい情報が返ってきた(ファイルがないとか、同名の新しいファイルがあるときはそちらの情報とか)。
→ exec関数でコマンドの実行でファイル名を変更したところ、php4でも、php5でもキャッシュされている情報が返ってきた。
つまり?
- パフォーマンス上の理由で、phpには一度参照したファイルの情報をキャッシュする機能があるのだが、注意しないと、実際とは違い情報を受け取ってしまうことがあるよ。
- unlink関数はキャッシュもクリアしてくれるよ。
- rename関数はphp4ではキャッシュをクリアし忘れているらしい。php5ではキャッシュをクリアしてくれるらしい。
- 独自の処理やコマンドの処理でファイルを削除することは避けて、phpの関数で処理をすると、キャッシュの方も調整してくれる。
- 他の処理などでファイルが書き換えられていたら、やっぱりキャッシュを参照してしまう。
まあ。。。キャッシュって大変ですね。
問題の確認用ソース
<?php
header('Content-Type: text/plain');
$log_file = '/tmp/filemtime.log';
// ログファイルにログを出力(無い場合は作られる)
error_log("test", 3, $log_file);
echo 'ファイルを書き換えました' . "\n";
// ログファイルの最終更新日時を出力
echo '現在の最終更新日時: ';
echo date('Y-m-d H:i:s', filemtime($log_file)) . "\n";
// ログファイルをリネーム(元の名前のファイルはなくなる)
rename($log_file, $log_file . '.bak');
//exec('mv ' . $log_file . ' ' . $log_file . '.bak');
echo 'ファイルをリネームしました' . "\n";
// 5秒お休み
sleep(5);
echo '5秒停止しました' . "\n";
// ログファイルにログを出力
error_log("test", 3, $log_file);
echo 'ファイルを書き換えました' . "\n";
// ログファイルの最終更新日時を出力
echo '現在の最終更新日時?: ';
echo date('Y-m-d H:i:s', filemtime($log_file)) . "\n";
//キャッシュをクリア
clearstatcache();
echo 'キャッシュしている結果をクリアしました' . "\n";
// ログファイルの最終更新日時を出力
echo '現在の最終更新日時!: ';
echo date('Y-m-d H:i:s', filemtime($log_file)) . "\n";
データベースを使うシステムばかりを扱っていると、ファイル操作をする機会がすっかりなくなってしまったりしますが、たまには思い出すとよいと思います。