This article reflects v3.4 and has not yet been revised

Improving Performance with Cache

Phalcon provides the Phalcon\Cache class allowing faster access to frequently used or already processed data. Phalcon\Cache is written in C, achieving higher performance and reducing the overhead when getting items from the backends. This class uses an internal structure of frontend and backend components. Front-end components act as input sources or interfaces, while backend components offer storage options to the class.

When to implement cache?

Although this component is very fast, implementing it in cases that are not needed could lead to a loss of performance rather than gain. We recommend you check this cases before using a cache:

  • 毎回同じ結果 (変更頻度の低い) を返す複雑な計算をしている
  • 多くのヘルパを利用し、生成される出力がほとんど同じである
  • 常にデータベースのデータにアクセスしており、これらのデータはほとんど変わらない

メモ キャッシュを実装した後でも、一定の期間でキャッシュのヒット率を確認するようにしましょう。 特にMemcacheやApcの場合、バックエンドが提供する関連ツールを使用すると、これを簡単に行うことができます。

Caching Behavior

The caching process is divided into 2 parts:

  • フロントエンド: この部分は、キーが期限切れになっていないかどうかをチェックし、保存する前にデータに追加の変換を実行します。
  • バックエンド: この部分は、フロントエンドが必要とするデータの通信、書き込み、読み取りを行います。

Factory

Instantiating frontend or backend adapters can be achieved by two ways:

Traditional way

<?php

use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Data as FrontData;

// 出力フロントエンドの作成。 ファイルを2日間キャッシュ。
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// 'Output'から'File'バックエンドにキャッシュするコンポーネントを作成する
// キャッシュファイルディレクトリを設定する - フォルダの値の最後に '/'を置くことが重要
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

or using the Factory object as follows:

<?php

use Phalcon\Cache\Frontend\Factory as FFactory;
use Phalcon\Cache\Backend\Factory as BFactory;

 $options = [
     'lifetime' => 172800,
     'adapter'  => 'data',
 ];
 $frontendCache = FFactory::load($options);


$options = [
    'cacheDir' => '../app/cache/',
    'prefix'   => 'app-data',
    'frontend' => $frontendCache,
    'adapter'  => 'file',
];

$backendCache = BFactory::load($options);

Caching Output Fragments

An output fragment is a piece of HTML or text that is cached as is and returned as is. The output is automatically captured from the ob_* functions or the PHP output so that it can be saved in the cache. The following example demonstrates such usage. It receives the output generated by PHP and stores it into a file. The contents of the file are refreshed every 172,800 seconds (2 days).

The implementation of this caching mechanism allows us to gain performance by not executing the helper Phalcon\Tag::linkTo() call whenever this piece of code is called.

<?php

use Phalcon\Tag;
use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Output as FrontOutput;

// 出力フロントエンドの作成。 ファイルを2日間キャッシュ。
$frontCache = new FrontOutput(
    [
        'lifetime' => 172800,
    ]
);

// 'Output'から'File'バックエンドにキャッシュするコンポーネントを作成する
// キャッシュファイルディレクトリを設定する - フォルダの値の最後に '/'を置いておくことが重要です
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

// キャッシュファイルを ../app/cache/my-cache.html で取得/設定する
$content = $cache->start('my-cache.html');

// $contentがnullの場合、コンテンツはキャッシュ用に生成されます。
if ($content === null) {
    // 日付と時刻を表示する
    echo date('r');

    // サインアップアクションへのリンクを生成する
    echo Tag::linkTo(
        [
            'user/signup',
            'Sign Up',
            'class' => 'signup-button',
        ]
    );

    // 出力をキャッシュファイルに保存する
    $cache->save();
} else {
    // キャッシュされた出力をechoする
    echo $content;
}

注意上記の例では、コードは以前と同じように出力されます。 キャッシュコンポーネントは透過的にその出力をキャプチャし、キャッシュファイルに保存します(キャッシュが生成されたとき)。または、前回の呼び出しで事前にコンパイルされたものをユーザーにレスポンスすることで、コストの高い処理を回避します。

Caching Arbitrary Data

Caching just data is equally important for your application. Caching can reduce database load by reusing commonly used (but not updated) data, thus speeding up your application.

File Backend Example

One of the caching adapters is File. The only key area for this adapter is the location of where the cache files will be stored. This is controlled by the cacheDir option which must have a backslash at the end of it.

<?php

use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Data as FrontData;

// Dataフロントエンドを使用して2日間ファイルをキャッシュする
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// 'Data'を'File'バックエンドにキャッシュするコンポーネントを作成する
// キャッシュファイルディレクトリを設定する - フォルダの値の最後に `/`を置くことが重要
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

$cacheKey = 'robots_order_id.cache';

// キャッシュされたレコードを取得してみる
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robotsはキャッシュの有効期限が切れているか、データが存在しないためnull
    // データベースを呼び出して変数に代入
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // キャッシュに保存
    $cache->save($cacheKey, $robots);
}

// $robotsを使う:)
foreach ($robots as $robot) {
   echo $robot->name, '\n';
}

Memcached Backend Example

The above example changes slightly (especially in terms of configuration) when we are using a Memcached backend.

<?php

use Phalcon\Cache\Frontend\Data as FrontData;
use Phalcon\Cache\Backend\Libmemcached as BackMemCached;

// データを1時間キャッシュする
$frontCache = new FrontData(
    [
        'lifetime' => 3600,
    ]
);

// 'Memcached'バックエンドに'データ'をキャッシュするコンポーネントを作成する
// Memcached接続設定
$cache = new BackMemCached(
    $frontCache,
    [
        'servers' => [
            [
                'host'   => '127.0.0.1',
                'port'   => '11211',
                'weight' => '1',
            ]
        ]
    ]
);

$cacheKey = 'robots_order_id.cache';

// キャッシュされたレコードを取得してみる
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robotsはキャッシュの有効期限が切れているか、データが存在しないためnull
    // データベースを呼び出して変数に代入
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // キャッシュに保存
    $cache->save($cacheKey, $robots);
}

// $robotsを使う :)
foreach ($robots as $robot) {
   echo $robot->name, '\n';
}

注意 save()を呼び、論理値を返します。これは成功 (true) または失敗 (false) を示します。 使用しているバックエンドに応じて、関連するログを調べて障害を特定する必要があります。

Querying the cache

The elements added to the cache are uniquely identified by a key. In the case of the File backend, the key is the actual filename. To retrieve data from the cache, we just have to call it using the unique key. If the key does not exist, the get method will return null.

<?php

// キー'myProducts'で製品を取得する
$products = $cache->get('myProducts');

If you want to know which keys are stored in the cache you could call the queryKeys method:

<?php

// キャッシュで使用されているすべてのキーを照会する
$keys = $cache->queryKeys();

foreach ($keys as $key) {
    $data = $cache->get($key);

    echo 'Key=', $key, ' Data=', $data;
}

// 'my-prefix'で始まるキャッシュ内のクエリキー
$keys = $cache->queryKeys('my-prefix');

Deleting data from the cache

There are times where you will need to forcibly invalidate a cache entry (due to an update in the cached data). The only requirement is to know the key that the data have been stored with.

<?php

// 対象のキーを持つアイテムを削除する
$cache->delete('someKey');

$keys = $cache->queryKeys();

// キャッシュからすべてのアイテムを削除する
foreach ($keys as $key) {
    $cache->delete($key);
}

Checking cache existence

It is possible to check if a cache already exists with a given key:

<?php

if ($cache->exists('someKey')) {
    echo $cache->get('someKey');
} else {
    echo 'Cache does not exists!';
}

Lifetime

A lifetime is a time in seconds that a cache could live without expire. By default, all the created caches use the lifetime set in the frontend creation. You can set a specific lifetime in the creation or retrieving of the data from the cache:

Setting the lifetime when retrieving:

<?php

$cacheKey = 'my.cache';

// 結果を取得するときにキャッシュを設定
$robots = $cache->get($cacheKey, 3600);

if ($robots === null) {
    $robots = 'some robots';

    // キャッシュに格納
    $cache->save($cacheKey, $robots);
}

Setting the lifetime when saving:

<?php

$cacheKey = 'my.cache';

$robots = $cache->get($cacheKey);

if ($robots === null) {
    $robots = 'some robots';

    // データを保存するときにキャッシュを設定
    $cache->save($cacheKey, $robots, 3600);
}

Multi-Level Cache

This feature of the cache component, allows the developer to implement a multi-level cache. This new feature is very useful because you can save the same data in several cache locations with different lifetimes, reading first from the one with the faster adapter and ending with the slowest one until the data expires:

<?php

use Phalcon\Cache\Multiple;
use Phalcon\Cache\Backend\Apc as ApcCache;
use Phalcon\Cache\Backend\File as FileCache;
use Phalcon\Cache\Frontend\Data as DataFrontend;
use Phalcon\Cache\Backend\Memcache as MemcacheCache;

$ultraFastFrontend = new DataFrontend(
    [
        'lifetime' => 3600,
    ]
);

$fastFrontend = new DataFrontend(
    [
        'lifetime' => 86400,
    ]
);

$slowFrontend = new DataFrontend(
    [
        'lifetime' => 604800,
    ]
);

// バックエンドは、早いものから遅いものへと順番に登録される
$cache = new Multiple(
    [
        new ApcCache(
            $ultraFastFrontend,
            [
                'prefix' => 'cache',
            ]
        ),
        new MemcacheCache(
            $fastFrontend,
            [
                'prefix' => 'cache',
                'host'   => 'localhost',
                'port'   => '11211',
            ]
        ),
        new FileCache(
            $slowFrontend,
            [
                'prefix'   => 'cache',
                'cacheDir' => '../app/cache/',
            ]
        ),
    ]
);

// すべてのバックエンドに保存
$cache->save('my-key', $data);

Frontend Adapters

The available frontend adapters that are used as interfaces or input sources to the cache are:

アダプター Description
Phalcon\Cache\Frontend\Output 標準のPHP出力から入力データを読み込みます。
Phalcon\Cache\Frontend\Data It’s used to cache any kind of PHP data (big arrays, objects, text, etc). Data is serialized before stored in the backend.
Phalcon\Cache\Frontend\Base64 It’s used to cache binary data. The data is serialized using base64_encode before be stored in the backend.
Phalcon\Cache\Frontend\Json Data is encoded in JSON before be stored in the backend. Decoded after be retrieved. This frontend is useful to share data with other languages or frameworks.
Phalcon\Cache\Frontend\Igbinary It’s used to cache any kind of PHP data (big arrays, objects, text, etc). Data is serialized using Igbinary before be stored in the backend.
Phalcon\Cache\Frontend\None あらゆる種類のPHPデータをシリアル化せずにキャッシュするために使用されます。

Implementing your own Frontend adapters

The Phalcon\Cache\FrontendInterface interface must be implemented in order to create your own frontend adapters or extend the existing ones.

Backend Adapters

The backend adapters available to store cache data are:

アダプター Description 情報 必須エクステンション
Phalcon\Cache\Backend\Apc Alternative PHP Cache (APC) にデータを格納します。 APC APC
Phalcon\Cache\Backend\Apcu データをAPCuに格納します(オペコードキャッシングなしのAPC) APCu APCu
Phalcon\Cache\Backend\File ローカルのプレーンファイルにデータを格納します。    
Phalcon\Cache\Backend\Libmemcached memcachedサーバーにデータを格納します。 Memcached Memcached
Phalcon\Cache\Backend\Memcache memcachedサーバーにデータを格納します。 Memcache Memcache
Phalcon\Cache\Backend\Memory Stores data in memory    
Phalcon\Cache\Backend\Mongo データをMongoデータベースに保存します。 MongoDB Mongo
Phalcon\Cache\Backend\Redis Redisにデータを格納します。 Redis Redis
Phalcon\Cache\Backend\Xcache XCacheにデータを格納します。 XCache XCache
NOTE In PHP 7 to use phalcon apc based adapter classes you needed to install apcu and apcu_bc package from pecl. Now in Phalcon 4.0.0 you can switch your <em>\Apc classes to </em>\Apcu and remove apcu_bc. Keep in mind that in Phalcon 4 we will most likely remove all *\Apc classes. {.alert.alert-warning}

Factory

There are many backend adapters (see Backend Adapters). The one you use will depend on the needs of your application. The following example loads the Backend Cache Adapter class using adapter option, if frontend will be provided as array it will call Frontend Cache Factory

<?php

use Phalcon\Cache\Backend\Factory;
use Phalcon\Cache\Frontend\Data;

$options = [
    'prefix'   => 'app-data',
    'frontend' => new Data(),
    'adapter'  => 'apc',
];
$backendCache = Factory::load($options);

Implementing your own Backend adapters

The Phalcon\Cache\BackendInterface interface must be implemented in order to create your own backend adapters or extend the existing ones.

File Backend Options

This backend will store cached content into files in the local server. The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。
cacheDir キャッシュされたファイルが置かれる書き込み可能なディレクトリ。

Libmemcached Backend Options

This backend will store cached content on a memcached server. Per default persistent memcached connection pools are used. The available options for this backend are:

General options

オプション Description
statsKey キャッシュされたキーの追跡に使用されます。
prefix キャッシュキーの前に自動的に付加される接頭辞。
persistent_id リクエストの間に存続するインスタンスを作成するには、persistent_idを使用してインスタンスの一意のIDを指定します。

Servers options

オプション Description
host memcachedホスト。
port memcachedポート番号。
weight weightパラメータは、キーを読み書きするサーバを決定するために使用されるコンシステントハッシュ法に影響します。

Client options

Used for setting Memcached options. See Memcached::setOptions for more.

<?php
use Phalcon\Cache\Backend\Libmemcached;
use Phalcon\Cache\Frontend\Data as FrontData;

// データを2日間キャッシュ
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// memcachedの接続オプションを設定するキャッシュを作成する
$cache = new Libmemcached(
    $frontCache,
    [
        'servers' => [
            [
                'host'   => '127.0.0.1',
                'port'   => 11211,
                'weight' => 1,
            ],
        ],
        'client' => [
            \Memcached::OPT_HASH       => \Memcached::HASH_MD5,
            \Memcached::OPT_PREFIX_KEY => 'prefix.',
        ],
        'persistent_id' => 'my_app_cache',
    ]
);

Memcache Backend Options

This backend will store cached content on a memcached server. The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。
host Memcachedホスト。
port memcachedポート番号。
persistent memcachedへの永続的な接続を作成するかどうか。

APC Backend Options

This backend will store cached content on Alternative PHP Cache (APC). The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。

APCU Backend Options

This backend will store cached content on Alternative PHP Cache (APCU). The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。

Mongo Backend Options

This backend will store cached content on a MongoDB server (MongoDB). The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。
server MongoDB接続文字列。
db Mongoデータベース名。
collection データベースのMongoコレクション。

XCache Backend Options

This backend will store cached content on XCache (XCache). The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。

Redis Backend Options

This backend will store cached content on a Redis server (Redis). The available options for this backend are:

オプション Description
prefix キャッシュキーの前に自動的に付加される接頭辞。
host Redisホスト。
port Redisポート。
auth パスワードで保護されたRedisサーバーに認証するためのパスワード。
persistent Redisへの永続的な接続を作成するかどうか。
index 使用するRedisデータベースのインデックス。

Phalcon Incubator には、このコンポーネントを利用するための複数のアダプターが用意されています。