Edited at

「キャッシュのせいです」とは言わせない!更新されたファイルだけ確実にキャッシュを無効化させる方法

More than 1 year has passed since last update.

「キャッシュのせいです。」「スーパリロードお願いします。」だなんて二度と言わない、言わせないために。


条件


  • 基本はキャッシュを有効にしたい

  • ファイル更新があった場合、そのファイルだけキャッシュを無視したい


前提知識

リクエストするファイルが同じでも、パラメータが異なると、違うファイルと認識されてキャッシュは無効化されます。

// 下記二つは別ファイルとして認識される

style.css?20170403
style.css?20170404

このパラメータを、ファイル更新があったときに、どうにかして自動更新したいわけです。


方法1:ファイルの最終更新日をパラメータに

PHP関数「filemtime()」を使い、そのファイルの最終更新日を取得してパラメータとします。


index.php

<link rel="stylesheet" href="/css/style.css?<?= filemtime('css/style.css'); ?>">


まさにタイトル通りのことが実現できていそうなものの、この関数には注意点があります。


注意: この関数の結果は キャッシュされます。


またもやキャッシュ。。

つまり、実際にはファイルが更新がされていても、この関数で返される結果にすぐに反映されない可能性があるようです。

実際に試しみてます。


実験1

下記スクリプトを実行→ファイルを編集→もう一度実行、してみる。

<?php 

echo filemtime('ファイル名');

すると、特にキャッシュされている様子はなく、filemtime()の結果がファイル編集前後で正常に変化しました。


実験2

次に下記スクリプトを実行。

一度のスクリプト内で実験1と同様のことを行い、最後の行で結果が変わったかどうかを比較しています。

<?php 

$old = filemtime('ファイル名');
file_put_contents('ファイル名', 'hoge');
$new = filemtime('ファイル名');
var_dump($old == $new);

するとtrueが返ってきてしまいました。

(=ファイルを変更したにもかかわらずfilemtime()の結果に反映されない!)

実験1と実験2の結果から、一度のスクリプト内の実行に関してのみfilemtime()の結果がキャッシュされてしまい、ファイルに更新があってもfilemtime()の結果が古いままになるようです。


実験3(おまけ)

下記のように、全然関係ない別のファイルに対するfilemtime()の実行をはさんでみると、、

<?php 

$old = filemtime('ファイル名');
filemtime('別のファイル名'); // ←追加
file_put_contents('ファイル名', 'hoge');
$new = filemtime('ファイル名');
var_dump($old == $new);

結果はfalse。

最後に実行したfilemtime()の結果だけがキャッシュされるみたいです。


結論

filemtime()の結果はキャッシュされると言っても、一度のスクリプト内に限った話なので、


index.php

<link rel="stylesheet" href="/css/style.css?<?= filemtime('css/style.css'); ?>">


のような使い方をする分には特に問題なさそうです。


方法2:gitのコミットハッシュをパラメータに

最初にfilemtime以外でなんとかしようと考えた末の苦肉の策。

gitのコミットハッシュをパラメータにつけてみました。


どっか

<?php 

function getCommitHash($file) {
return exec('git rev-parse --short :'.$file);
}
// ↑ユーザー入力を引数に渡したらダメゼッタイ


index.php

<link rel="stylesheet" href="/css/style.css?<?= getCommitHash('css/style.css'); ?>">


exec関数使いたくないし、gitに依存するのも気持ち悪いです。

方法1が問題ないとわかった以上、こっちを選ぶ理由はありません。