COMPANY SERVICE STAFF BLOG NEWS CONTACT

STAFF BLOG

スタッフブログ

TECHNICAL

テクログ

2025.01.07

FuelPHPがDB問い合わせの結果を{キャッシュするとき、キャッシュから結果を得るとき}にどういう処理をしているかを追う

テクログphp

FuelPHP の公式サイト

https://fuelphp.com/

追い方

Docker コンテナを使って windows 10 のローカル環境で追います。

FuelPHPのバージョンやキャッシュの設定

\Fuel\Core\Debug::dump(
    \Fuel\Core\Fuel::VERSION,
    \Fuel\Core\Config::get('cache.driver')
);
/**
 * APPPATH/classes/controller/welcome.php @ line: 36
 * Variable #1:
 *   (String): "1.9-dev" (7 characters)
 * Variable #2:
 *   (String): "file" (4 characters)
 **/

DBキャッシュはファイルとしてプロジェクトのディレクトリに作成されます。

OS, PHP のバージョンは最後に記載しています。あまり関係ない気がしましたので。

DB問い合わせの結果をキャッシュをするときの動作を表面的に見る

適当な画面でDB問い合わせ処理を実行しています。

execute メソッドを実行する前に cached メソッドを呼ぶことで DB 問い合わせの結果をキャッシュすることができます。

ここでは cached メソッドの引数として秒数を表す数値を渡しています。

※ この記事ではキャッシュを取得するときに時間経過をどのように判定しているかは追っていません。

public function action_index()
{
    $result = \Fuel\Core\DB::query('SELECT 1 FROM zikken WHERE id = 1')
    ->cached(3600)
    ->execute();

    die;

すると、fuel/app/cache/db/2aa61195a4e449f121b785f9b75d27e1.cache のようにファイルが作成されます。

cached メソッドの中を見る

cached メソッドの中身を見るとインスタンス変数に値を設定しているだけです。

実際にキャッシュを行うときは execute メソッドの中で実行されているようです。

/**
 * Enables the query to be cached for a specified amount of time.
 *
 * @param   integer $lifetime  number of seconds to cache or null for default
 * @param   string  $cache_key name of the cache key to be used or null for default
 * @param   boolean $cache_all if true, cache all results, even empty ones
 *
 * @return  $this
 */
public function cached($lifetime = null, $cache_key = null, $cache_all = true)
{
    $this->_lifetime = $lifetime;
    $this->_cache_all = (bool) $cache_all;
    is_string($cache_key) and $this->_cache_key = $cache_key;

    return $this;
}

execute メソッドの中を見る(キャッシュファイル名を決定 & キャッシュがある場合に return するところ)

/**
 * Execute the current query on the given database.
 *
 * @param   mixed   $db Database instance or name of instance
 *
 * @return  object   Database_Result for SELECT queries
 * @return  mixed    the insert id for INSERT queries
 * @return  integer  number of affected rows for all other queries
 */
public function execute($db = null)
{
    ~省略~
    if ($caching and ! empty($this->_lifetime) and $this->_type === \DB::SELECT)
    {
        $cache_key = empty($this->_cache_key) ?
            'db.'.md5('Database_Connection::query("'.$db.'", "'.$sql.'")') : $this->_cache_key;
        $cache = \Cache::forge($cache_key);
        try
        {
            return $db->cache($cache->get(), $sql, $this->_as_object);
        }
        catch (\CacheNotFoundException $e) {}
    }
    ~~ 省略 DB接続してSQLを投げる処理が続きます ~~

キャッシュファイル名は 「’db.’.md5(‘Database_Connection::query(“‘.$db.'”, “‘.$sql.'”)’)」 によって決定しています。「cached(3600, ‘example’)」 のように cached メソッドの第2引数に名前を設定することで、キャッシュファイル名を明示的に設定可能です。

※ この場合でも example.cache というファイルが作成されますが、拡張子の .cache がどこで付くのかは追っていません。

DBキャッシュが存在していればSQL実行をせずに以下の部分で return しています。 try catch 内にあるのでキャッシュが無ければ止まらずに処理が進みます。

try
{
    return $db->cache($cache->get(), $sql, $this->_as_object);
}
catch (\CacheNotFoundException $e) {}

execute メソッドの中を見る(キャッシュを作成する)

先程の処理の続きにキャッシュを作成する処理があります。

SQL実行後の分岐内に 「$result->as_array()」 をキャッシュしています。このキャッシュを行う処理によって、次回同じ条件(引数の$dbとSQLが同じ場合)のDB問い合わせを行うときには、実際にDB問い合わせをせずにキャッシュファイルから結果を取得できます。

public function execute($db = null)
{
    ~省略~
    // Execute the query
    \DB::$query_count++;
    $result = $db->query($this->_type, $sql, $this->_as_object, $caching);

    // Cache the result if needed
    if (isset($cache) and ($this->_cache_all or $result->count()))
    {
        $cache->set_expiration($this->_lifetime)->set_contents($result->as_array())->set();
    }

    return $result;
}

おわり

この記事では追っていない部分もありますが、DB問い合わせのキャッシュについて処理を追っていきました。

普段使っている処理を追ってみると解像度が上がり、新鮮味も感じることができるので、充実度が少し上がります。充実度が欲しい人は、普段の処理を追うことで得られるかもしれません。

お疲れ様でした。

おまけ キャッシュファイル名にディレクトリを指定してみる

「mydirectory.zikken1」 のようにドットを使うことでディレクトリ指定できます。

この機能は公式ドキュメントで言うと以下に記載されています。

https://fuelphp.com/docs/classes/database/usage.html#/caching

たとえば↓↓このように実行すると、

public function action_index()
{
    $result = \Fuel\Core\DB::query('SELECT 1 FROM zikken WHERE id = 1')
    ->cached(3600, 'mydirectory.zikken1')
    ->execute();

    die;

以下のように mydirectory/zikken1.cache が作成されます。

この機能を利用すれば、「なるべく残しておきたいキャッシュ」と「そうでないキャッシュ」をディレクトリを分けて管理できるので、使い所によっては便利かもしれないです。

バージョン

OS

bash-5.2# cat /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023.6.20241121"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/"
DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/"
SUPPORT_URL="https://aws.amazon.com/premiumsupport/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
VENDOR_NAME="AWS"
VENDOR_URL="https://aws.amazon.com/"
SUPPORT_END="2028-03-15"
bash-5.2#

php

bash-5.2# php -v
PHP 8.3.10 (cli) (built: Jul 30 2024 13:44:37) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.10, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.10, Copyright (c), by Zend Technologies
bash-5.2#

この記事を書いた人

インドア

入社年2017年

出身地埼玉県

業務内容開発

特技・趣味映画・アニメ鑑賞、ゲーム、散歩、勉強

テクログに関する記事一覧

TOP