Caching
class
Cake\Cache\Cache
Caching can be used to make reading from expensive or slow resources faster, by maintaining a second copy of the required data in a faster or closer storage system. For example, you can store the results of expensive queries, or remote webservice access that doesn't frequently change in a cache. Once in the cache, reading data from the cache is much cheaper than accessing the remote resource.
Caching in CakePHP is facilitated by the Cache
class. This class provides a static interface and uniform API to interact with various Caching implementations. CakePHP provides several cache engines, and provides a simple interface if you need to build your own backend. The built-in caching engines are:
File
File cache is a simple cache that uses local files. It is the slowest cache engine, and doesn't provide as many features for atomic operations. However, since disk storage is often quite cheap, storing large objects, or elements that are infrequently written work well in files.Memcached
Uses the Memcached extension.Redis
Uses the phpredis extension. Redis provides a fast and persistent cache system similar to Memcached, also provides atomic operations.Apcu
APCu cache uses the PHP APCu extension. This extension uses shared memory on the webserver to store objects. This makes it very fast, and able to provide atomic read/write features.Array
Stores all data in an array. This engine does not provide persistent storage and is intended for use in application test suites.Null
The null engine doesn't actually store anything and fails all read operations.
Regardless of the CacheEngine you choose to use, your application interacts with Cake\Cache\Cache
.
Configuring Cache Engines
Your application can configure any number of 'engines' during its bootstrap process. Cache engine configurations are defined in config/app.php.
For optimal performance CakePHP requires two cache engines to be defined.
_cake_core_
is used for storing file maps, and parsed results of Internationalization & Localization files._cake_model_
, is used to store schema descriptions for your applications models.
Using multiple engine configurations also lets you incrementally change the storage as needed. For example in your config/app.php you could put the following:
// ...
'Cache' => [
'short' => [
'className' => 'File',
'duration' => '+1 hours',
'path' => CACHE,
'prefix' => 'cake_short_',
],
// Using a fully namespaced name.
'long' => [
'className' => 'Cake\Cache\Engine\FileEngine',
'duration' => '+1 week',
'probability' => 100,
'path' => CACHE . 'long' . DS,
],
]
// ...
Configuration options can also be provided as a DSN
string. This is useful when working with environment variables or PaaS
providers:
Cache::setConfig('short', [
'url' => 'memcached://user:password@cache-host/?timeout=3600&prefix=myapp_',
]);
When using a DSN string you can define any additional parameters/options as query string arguments.
You can also configure Cache engines at runtime:
// Using a short name
Cache::setConfig('short', [
'className' => 'File',
'duration' => '+1 hours',
'path' => CACHE,
'prefix' => 'cake_short_'
]);
// Using a fully namespaced name.
Cache::setConfig('long', [
'className' => 'Cake\Cache\Engine\FileEngine',
'duration' => '+1 week',
'probability' => 100,
'path' => CACHE . 'long' . DS,
]);
// Using a constructed object.
$object = new FileEngine($config);
Cache::setConfig('other', $object);
The name of these engine configurations ('short' and 'long') are used as the $config
parameter for Cake\Cache\Cache::write()
and Cake\Cache\Cache::read()
. When configuring cache engines you can refer to the class name using the following syntaxes:
// Short name (in App\ or Cake namespaces)
Cache::setConfig('long', ['className' => 'File']);
// Plugin short name
Cache::setConfig('long', ['className' => 'MyPlugin.SuperCache']);
// Full namespace
Cache::setConfig('long', ['className' => 'Cake\Cache\Engine\FileEngine']);
// An object implementing CacheEngineInterface
Cache::setConfig('long', ['className' => $myCache]);
NOTE
When using the FileEngine you might need to use the mask
option to ensure cache files are made with the correct permissions.
Engine Options
Each engine accepts the following options:
duration
Specify how long items in this cache configuration last. Specified as astrtotime()
compatible expression.groups
List of groups or 'tags' associated to every key stored in this config. Useful when you need to delete a subset of data from a cache.prefix
Prepended to all entries. Good for when you need to share a keyspace with either another cache config or another application.probability
Probability of hitting a cache gc cleanup. Setting to 0 will disableCache::gc()
from ever being called automatically.
FileEngine Options
FileEngine uses the following engine specific options:
isWindows
Automatically populated with whether the host is windows or notlock
Should files be locked before writing to them?mask
The mask used for created filespath
Path to where cachefiles should be saved. Defaults to system's temp dir.
RedisEngine Options
RedisEngine uses the following engine specific options:
port
The port your Redis server is running on.host
The host your Redis server is running on.database
The database number to use for connection.password
Redis server password.persistent
Should a persistent connection be made to Redis.timeout
Connection timeout for Redis.unix_socket
Path to a unix socket for Redis.tls
Connect to redis over TLS.ssl_key
The ssl private key used for TLS connections.ssl_ca
The ssl certificate authority file for TLS connections.ssl_cert
The ssl certificate used for TLS connections.
Added in version 5.1.0
TLS connections were added in 5.1
MemcacheEngine Options
compress
Whether to compress data.username
Login to access the Memcache server.password
Password to access the Memcache server.persistent
The name of the persistent connection. All configurations using the same persistent value will share a single underlying connection.serialize
The serializer engine used to serialize data. Available engines are php, igbinary and json. Beside php, the memcached extension must be compiled with the appropriate serializer support.servers
String or array of memcached servers. If an array MemcacheEngine will use them as a pool.duration
Be aware that any duration greater than 30 days will be treated as real Unix time value rather than an offset from current time.options
Additional options for the memcached client. Should be an array of option => value. Use the\Memcached::OPT_*
constants as keys.
Configuring Cache Fallbacks
In the event that an engine is not available, such as the FileEngine
trying to write to an unwritable folder or the RedisEngine
failing to connect to Redis, the engine will fall back to the noop NullEngine
and trigger a loggable error. This prevents the application from throwing an uncaught exception due to cache failure.
You can configure Cache configurations to fall back to a specified config using the fallback
configuration key:
Cache::setConfig('redis', [
'className' => 'Redis',
'duration' => '+1 hours',
'prefix' => 'cake_redis_',
'host' => '127.0.0.1',
'port' => 6379,
'fallback' => 'default',
]);
If initializing the RedisEngine
instance fails, the redis
cache configuration would fall back to using the default
cache configuration. If initializing the engine for the default
cache configuration also fails, in this scenario the engine would fall back once again to the NullEngine
and prevent the application from throwing an uncaught exception.
You can turn off cache fallbacks with false
:
Cache::setConfig('redis', [
'className' => 'Redis',
'duration' => '+1 hours',
'prefix' => 'cake_redis_',
'host' => '127.0.0.1',
'port' => 6379,
'fallback' => false
]);
When there is no fallback cache failures will be raised as exceptions.
Removing Configured Cache Engines
Once a configuration is created you cannot change it. Instead you should drop the configuration and re-create it using Cake\Cache\Cache::drop()
and Cake\Cache\Cache::setConfig()
. Dropping a cache engine will remove the config and destroy the adapter if it was constructed.
Writing to a Cache
Cache::write()
will write a $value to the Cache. You can read or delete this value later by referring to it by $key
. You may specify an optional configuration to store the cache in as well. If no $config
is specified, default will be used. Cache::write()
can store any type of object and is ideal for storing results of model finds:
$posts = Cache::read('posts');
if ($posts === null) {
$posts = $someService->getAllPosts();
Cache::write('posts', $posts);
}
Using Cache::write()
and Cache::read()
to reduce the number of trips made to the database to fetch posts.
NOTE
If you plan to cache the result of queries made with the CakePHP ORM, it is better to use the built-in cache capabilities of the Query object as described in the Caching Query Results section
Writing Multiple Keys at Once
You may find yourself needing to write multiple cache keys at once. While you can use multiple calls to write()
, writeMany()
allows CakePHP to use more efficient storage APIs where available. For example using writeMany()
save multiple network connections when using Memcached:
$result = Cache::writeMany([
'article-' . $slug => $article,
'article-' . $slug . '-comments' => $comments
]);
// $result will contain
['article-first-post' => true, 'article-first-post-comments' => true]
Atomic writes
Using Cache::add()
will let you atomically set a key to a value if the key does not already exist in the cache. If the key already exists in the cache backend or the write fails, add()
will return false
:
// Set a key to act as a lock
$result = Cache::add($lockKey, true);
if (!$result) {
return;
}
// Do an action where there can only be one process active at a time.
// Remove the lock key.
Cache::delete($lockKey);
WARNING
File based caching does not support atomic writes.
Read Through Caching
Cache helps with read-through caching. If the named cache key exists, it will be returned. If the key does not exist, the callable will be invoked and the results stored in the cache at the provided key.
For example, you often want to cache remote service call results. You could use remember()
to make this simple:
class IssueService
{
public function allIssues($repo)
{
return Cache::remember($repo . '-issues', function () use ($repo) {
return $this->fetchAll($repo);
});
}
}
Reading From a Cache
Cache::read()
is used to read the cached value stored under $key
from the $config
. If $config
is null the default config will be used. Cache::read()
will return the cached value if it is a valid cache or null
if the cache has expired or doesn't exist. Use strict comparison operators ===
or !==
to check the success of the Cache::read()
operation.
For example:
$cloud = Cache::read('cloud');
if ($cloud !== null) {
return $cloud;
}
// Generate cloud data
// ...
// Store data in cache
Cache::write('cloud', $cloud);
return $cloud;
Or if you are using another cache configuration called short
, you can specify it in Cache::read()
and Cache::write()
calls as below:
// Read key "cloud", but from short configuration instead of default
$cloud = Cache::read('cloud', 'short');
if ($cloud === null) {
// Generate cloud data
// ...
// Store data in cache, using short cache configuration instead of default
Cache::write('cloud', $cloud, 'short');
}
return $cloud;
Reading Multiple Keys at Once
After you've written multiple keys at once, you'll probably want to read them as well. While you could use multiple calls to read()
, readMany()
allows CakePHP to use more efficient storage APIs where available. For example using readMany()
save multiple network connections when using Memcached:
$result = Cache::readMany([
'article-' . $slug,
'article-' . $slug . '-comments'
]);
// $result will contain
['article-first-post' => '...', 'article-first-post-comments' => '...']
Deleting From a Cache
Cache::delete()
will allow you to completely remove a cached object from the store:
// Remove a key
Cache::delete('my_key');
As of 4.4.0, the RedisEngine
also provides a deleteAsync()
method which uses the UNLINK
operation to remove cache keys:
Cache::pool('redis')->deleteAsync('my_key');
Deleting Multiple Keys at Once
After you've written multiple keys at once, you may want to delete them. While you could use multiple calls to delete()
, deleteMany()
allows CakePHP to use more efficient storage APIs where available. For example using deleteMany()
save multiple network connections when using Memcached:
$result = Cache::deleteMany([
'article-' . $slug,
'article-' . $slug . '-comments'
]);
// $result will contain
['article-first-post' => true, 'article-first-post-comments' => true]
Clearing Cached Data
Destroy all cached values for a cache configuration. In engines like: Apcu, Memcached, the cache configuration's prefix is used to remove cache entries. Make sure that different cache configurations have different prefixes:
// Will clear all keys.
Cache::clear();
As of 4.4.0, the RedisEngine
also provides a clearBlocking()
method which uses the UNLINK
operation to remove cache keys:
Cache::pool('redis')->clearBlocking();
NOTE
Because APCu uses isolated caches for webserver and CLI they have to be cleared separately (CLI cannot clear webserver and vice versa).
Using Cache to Store Counters
Counters in your application are good candidates for storage in a cache. As an example, a simple countdown for remaining 'slots' in a contest could be stored in Cache. The Cache class exposes atomic ways to increment/decrement counter values. Atomic operations are important for these values as it reduces the risk of contention, and ability for two users to simultaneously lower the value by one, resulting in an incorrect value.
After setting an integer value you can manipulate it using increment()
and decrement()
:
Cache::write('initial_count', 10);
// Later on
Cache::decrement('initial_count');
// Or
Cache::increment('initial_count');
NOTE
Incrementing and decrementing do not work with FileEngine. You should use APCu, Redis or Memcached instead.
Using Cache to Store Common Query Results
You can greatly improve the performance of your application by putting results that infrequently change, or that are subject to heavy reads into the cache. A perfect example of this are the results from Cake\ORM\Table::find()
. The Query object allows you to cache results using the cache()
method. See the Caching Query Results section for more information.
Using Groups
Sometimes you will want to mark multiple cache entries to belong to certain group or namespace. This is a common requirement for mass-invalidating keys whenever some information changes that is shared among all entries in the same group. This is possible by declaring the groups in cache configuration:
Cache::setConfig('site_home', [
'className' => 'Redis',
'duration' => '+999 days',
'groups' => ['comment', 'article'],
]);
method
Cake\Cache\Cache::clearGroup($group, $config = 'default')
Let's say you want to store the HTML generated for your homepage in cache, but would also want to automatically invalidate this cache every time a comment or post is added to your database. By adding the groups comment
and article
, we have effectively tagged any key stored into this cache configuration with both group names.
For instance, whenever a new post is added, we could tell the Cache engine to remove all entries associated to the article
group:
// src/Model/Table/ArticlesTable.php
public function afterSave($event, $entity, $options = [])
{
if ($entity->isNew()) {
Cache::clearGroup('article', 'site_home');
}
}
groupConfigs()
can be used to retrieve mapping between group and configurations, i.e.: having the same group:
// src/Model/Table/ArticlesTable.php
/**
* A variation of previous example that clears all Cache configurations
* having the same group
*/
public function afterSave($event, $entity, $options = [])
{
if ($entity->isNew()) {
$configs = Cache::groupConfigs('article');
foreach ($configs['article'] as $config) {
Cache::clearGroup('article', $config);
}
}
}
Groups are shared across all cache configs using the same engine and same prefix. If you are using groups and want to take advantage of group deletion, choose a common prefix for all your configs.
Globally Enable or Disable Cache
You may need to disable all Cache read & writes when trying to figure out cache expiration related issues. You can do this using enable()
and disable()
:
// Disable all cache reads, and cache writes.
Cache::disable();
Once disabled, all reads and writes will return null
.
Once disabled, you can use enable()
to re-enable caching:
// Re-enable all cache reads, and cache writes.
Cache::enable();
If you need to check on the state of Cache, you can use enabled()
.
Creating a Cache Engine
You can provide custom Cache
engines in App\Cache\Engine
as well as in plugins using $plugin\Cache\Engine
. Cache engines must be in a cache directory. If you had a cache engine named MyCustomCacheEngine
it would be placed in either src/Cache/Engine/MyCustomCacheEngine.php. Or in plugins/MyPlugin/src/Cache/Engine/MyCustomCacheEngine.php as part of a plugin. Cache configs from plugins need to use the plugin dot syntax:
Cache::setConfig('custom', [
'className' => 'MyPlugin.MyCustomCache',
// ...
]);
Custom Cache engines must extend Cake\Cache\CacheEngine
which defines a number of abstract methods as well as provides a few initialization methods.
The required API for a CacheEngine is
class
Cake\Cache\CacheEngine
method
Cake\Cache\CacheEngine::write($key, $value)
method
Cake\Cache\CacheEngine::read($key)
method
Cake\Cache\CacheEngine::delete($key)
method
Cake\Cache\CacheEngine::clear($check)
method
Cake\Cache\CacheEngine::clearGroup($group)
method
Cake\Cache\CacheEngine::decrement($key, $offset = 1)
method
Cake\Cache\CacheEngine::increment($key, $offset = 1)