clearstatcache()のはなし
前々回、前回のrealpath cacheの話は実のところこの話の前振り。(なんだけど、前振りの方が長くて役立つ。
clearstatcache()を叩くと、realpath cacheもクリアされるよというのが前回までの話なのですが、clearstatcache()の関数リファレンスにはこのように説明が書かれています。
PHP: clearstatcache - Manualvoid clearstatcache( void )
stat やlstat、またはその他の関数(後述)を使用すると、PHPはパフォーマンス向上のために それらの関数の戻り値をキャッシュします。しかし、ケースによっては、 キャッシュされた情報を消去したい場合もあるでしょう。例えば、一つのスクリプト上で同じファイルが何度もチェックされ、そのファイルが変更されたり削除されたりする可能性がある場合、ステータスキャッシュを消去しなければならないと感じるでしょう。 このようなケースでは、clearstatcache()を使用することでファイルの情報に関してPHPが持っているキャッシュをクリアすることができます。
PHPは存在しないファイルについての情報はキャッシュしないことにも注意してください。もし存在しないファイルに対して file_exists() をコールする場合、ファイルを作成するまで この関数は FALSE を返します。もしファイルを作成した場合、たとえファイルを削除したとしても TRUE を返します。しかし、unlink() はキャッシュを自動的にクリアします。
注意: この関数は特定のファイルに関する情報をキャッシュします。したがって、 同じファイルについて複数回の操作を行いそのファイルに関する情報を キャッシュされないようにするためには、 clearstatcache()をコールするだけです。
影響を受ける関数を以下に示します。 stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), および fileperms().
ファイルは最後の1ファイルのみがキャッシュされる
説明を読むと、なるほどこれらの関数を叩くと結果がキャッシュされるのね、というのは分かるのですが、この中で注意すべき点は、
この関数は特定のファイルに関する情報をキャッシュします。 (This function caches information about specific filenames)
というところ。実は、キャッシュされるのは最終の1ファイルのみです。 (この原文から読み取れないけどね)
なので、このキャッシュは、どちらかというと、
if (is_dir($filename) && filemtime($filename) < $hoge) { ... }
的な利用を考慮しているものということ。
例を挙げると、
for ($i = 0; $i < 10000; ++$i) { filetype($file1); filetype($file2); }
と、
for ($i = 0; $i < 10000; ++$i) { filetype($file1); } for ($i = 0; $i < 10000; ++$i) { filetype($file2); }
とでは、後者のがキャッシュが有効で圧倒的に速い。(意味ないけどね)
リファレンス通りの動作をしているのは、Windows以外では、PHP4.3未満
困りはしないですが、リファレンスに説明されている通りに動作するのは、PHP4.3未満です。
PHP4.3.0以降では、Windows環境以外では、is_readable() / is_writable() / is_executable() / file_exists() 関数については、access(2)をコール (現在は、VCWD_ACCESS()をコール)するようになっており、これは上記キャッシュの対象外になります。
また、PHP5以降では、php_stream_stat_path()を内部的に呼び出すようになり、ローカルのファイル以外でもstat()系の値を返却できるようになりましたが、ローカルファイル以外はキャッシュ対応外です。(対応できないこともないと思うけど)
PHP5.1以降からはrealpathのcacheをクリアするようになった
これは前に書いたとおり。
clearstatcache()の実装はシンプルで、php_clear_stat_cache()を呼び出しているだけ。
717 /* {{{ proto void clearstatcache(void) 718 Clear file stat cache */ 719 PHP_FUNCTION(clearstatcache) 720 { 721 if (ZEND_NUM_ARGS()) { 722 WRONG_PARAM_COUNT; 723 } 724 php_clear_stat_cache(TSRMLS_C); 725 } 726 /* }}} */
で、BG(CurrentStatFile)
と BG(CurrentLStatFile)
をクリアして、realpath_cache_clean()で全クリア
701 /* {{{ php_clear_stat_cache() 702 */ 703 PHPAPI void php_clear_stat_cache(TSRMLS_D) 704 { 705 if (BG(CurrentStatFile)) { 706 efree(BG(CurrentStatFile)); 707 BG(CurrentStatFile) = NULL; 708 } 709 if (BG(CurrentLStatFile)) { 710 efree(BG(CurrentLStatFile)); 711 BG(CurrentLStatFile) = NULL; 712 } 713 realpath_cache_clean(TSRMLS_C); 714 }
BG(CurrentStatFile)
と BG(CurrentLStatFile)
には、ファイル名が格納されていて、stat/lstatの結果を格納してあるのは、BG(sb)
で、ファイル名が一致してればキャッシュを返し、そうでなければ普通に呼び出してキャッシュを更新するという流れです。
しかし、realpath_cache_clean()をここに入れるのはどうなのよというのもあるし、マニュアルに書いてないのもどうなのよと思った。何か理由があったり、自分の勘違いだったりするのかなぁ。。
[2009/06/23追記] PHP5.3からはデフォルトではrealpathのcacheをクリアしないようになった
5.1から挙動が変わったclearstatcache()ですが、5.3ではデフォルト動作が従来通りに戻ります。要は、realpath cacheのクリアをしなくなりました。
5.3では、オプション引数が2つ追加されます。
void clearstatcache ([ bool $clear_realpath_cache= false [, string $filename ] ] )
PHP: clearstatcache - Manual
- clear_realpath_cache
- realpath cacheをクリアするか (デフォルトはfalse)
- filename
- clear_realpath_cacheがtrueのときに、ファイル名を指定して、そのファイルのみのキャッシュをクリア
なんというか、、関数分ければいいのに。。