QuickAnswer
by

CSSや画像ファイル更新時にブラウザキャッシュを取得させない

CSSや画像ファイル更新時にブラウザキャッシュを取得させない

ウェブサイトで、CSSや画像やJavaScriptの各ファイルを更新したときに、そのままデータをアップロードしてもブラウザのキャッシュが使用されて正しい表示にならない。

各ファイルのリンク箇所にクエリパラメータを付加すれば新しいファイルとして扱われるためキャッシュは使用されない。

変更前

<link rel="stylesheet" href="./style.css">

変更後

<link rel="stylesheet" href="./style.css?1">

ただし、多くのファイルを変更したときはどのファイルを更新したのか忘れたり漏れが出る。

そこで、一気に全リンクを更新してしまうPHPプログラムを作成した。

update_query_parameters.php

<?php
declare(strict_types=1);

/**
 * 各ファイルリンクのクエリパラメータを更新する
 * @author ao-system, Inc.
 * @date 2024-08-02
 */
class UpdateQueryParameters {

    /**
     * 置換対象の拡張子
     * @const array
     */
    private const EXTENSIONS = ['css','js','webp','avif','svg','png','jpg','gif','webm','mp4'];

    /**
     * 置換前のパターンが設定される
     * @var array
     */
    private array $patterns = [];

    /**
     * 置換後の文字列が設定される
     * @var array
     */
    private array $replacements = [];

    /**
     * 調査対象外にするパスの配列
     * @var array
     */
    private array $excludePaths = [];

    /**
     * 調査開始位置
     * @var string
     */
    private string $directory = './';

    /**
     * constructor
     */
    public function __construct() {
        //何もしない
    }

    /**
     * 付加するクエリパラメータをset
     * @param int $number 数値
     * @return void
     */
    public function setNumber(int $number = 0): void {
        $this->patterns = [];
        $this->replacements = [];
        foreach (self::EXTENSIONS as $ext) {
            $this->patterns[] = '/\.' . $ext . '(\?[\d]+)?"/';
            $this->replacements[] = '.' . $ext . '?' . $number . '"';
        }
    }

    /**
     * 調査除外のパス配列をset
     * パスの末尾の「/」の有無はどちらでもよいが文字列で比較されるため注意
     * @param array $paths
     * @return void
     */
    public function setExcludePaths(array $paths = []): void {
        $this->excludePaths = array_map(function($path) {
            return str_replace('\\', '/', $path);    //ディレクトリセパレータは「/」に統一
        }, $paths);
    }

    /**
     * 調査対象のルートパスをset
     * パスの末尾の「/」の有無はどちらでもよい
     * @param string $dir
     * @return void
     */
    public function setDirectory(string $dir = './'): void {
        $this->directory = str_replace('\\', '/', $dir);    //ディレクトリセパレータは「/」に統一
    }

    /**
     * 置換実施
     * @param bool $replaceFlag 置換する場合はtrue
     * @return void
     */
    public function replace(bool $replaceFlag = false): void {
        $matchCount = 0;
        $replaceCount = 0;
        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory));
        foreach ($rii as $file) {
            if ($file->isDir()) {
                continue;
            }
            $filePath = str_replace('\\', '/', $file->getPathname());    //ディレクトリセパレータは「/」に統一
            //除外パスが含まれていれば$exclude=trueにする
            $exclude = false;
            foreach ($this->excludePaths as $excludePath) {
                if (strpos($filePath, $excludePath) !== false) {
                    $exclude = true;
                    break;
                }
            }
            //phpファイルを処理
            if ($exclude == false && $file->getExtension() === 'php') {
                $content = file_get_contents($filePath);
                //一致を表示
                foreach ($this->patterns as $pattern) {
                    if (preg_match_all($pattern, $content, $matches)) {
                        foreach ($matches[0] as $match) {
                            print str_pad(strval($matchCount + 1),3,'0',STR_PAD_LEFT) . ": " . $match . "\t:\t" . $filePath . "\n";
                            $matchCount += 1;
                        }
                    }
                }
                if ($replaceFlag) {
                    //一致を置換
                    $count = 0;
                    $updatedContent = preg_replace($this->patterns, $this->replacements, $content, -1, $count);
                    $replaceCount += $count;
                    //ファイルを書き換える
                    file_put_contents($filePath, $updatedContent);
                }
            }
        }
        //結果表示
        print 'resuut matchs:' . $matchCount . ' replaces:' . $replaceCount . "\n";
    }
}

$updateQueryParameters = new UpdateQueryParameters();
$updateQueryParameters->setDirectory('./httpdocs/');
$updateQueryParameters->setExcludePaths(['./httpdocs/_tmp/']);
$updateQueryParameters->setNumber(2);
$updateQueryParameters->replace(true);    //true指定で置換
$updateQueryParameters->replace(false);    //false指定で再確認

なお、CSS内のリンクは更新されないので、必要ならこのスニペットを拡張して欲しい。 以下など:

main {
    background-image(./image/back.svg);
}

それと、見ての通り処理されるのは *.php に限られる。

実行方法はupdate_query_parameters.phpの中身を書き換えた上でこんな感じで:

php update_query_parameters.php
CONTENTS