Cache¶
Overview¶
The Phalcon\Cache namespace offers a Cache component, that implements the PSR-16 interface, making it compatible with any component that requires that interface for its cache.
Frequently used data or already processed/calculated data, can be stored in a cache storage for easier and faster retrieval. Since Phalcon\Cache components are written in Zephir, and therefore compiled as C code, they can achieve higher performance, while reducing the overhead that comes with getting data from any storage container. Some examples that warrant the use of cache are:
- You are making complex calculations and the output does not change frequently
- You are producing HTML using the same data all the time (same HTML)
- You are accessing database data constantly which does not change often.
NOTE
Even after implementing the cache, you should always check the hit ratio of your cache backend over a period of time, to ensure that your cache strategy is optimal.
Phalcon\Cache components rely on Phalcon\Storage
components. Phalcon\Storage
is split into two categories: Serializers and Adapters.
Cache¶
In order to instantiate a new Phalcon\Cache component, you will need to pass a Phalcon\Cache\Adapter\*
class in it or one that implements the Phalcon\Cache\Adapter\AdapterInterface. For a detailed explanation on adapters and serializers, see below.
<?php
use Phalcon\Cache;
use Phalcon\Cache\AdapterFactory;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$adapterFactory = new AdapterFactory($serializerFactory);
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200
];
$adapter = $adapterFactory->newInstance('apcu', $options);
$cache = new Cache($adapter);
Operations¶
Since the cache component is PSR-16 compatible it implements all the necessary methods to satisfy the PSR-16 interfaces. Each Cache component contains a supplied Cache adapter which in turn is responsible for all operations.
get
- getMultiple
¶
To get data from the cache you need to call the get()
method with a key and a default value. If the key exists or it has not been expired, the data stored in it will be returned. Alternatively the passed defaultValue
will be returned (default null
).
If you wish to retrieve more than one key with one call, you can call getMultiple()
, passing an array with the keys needed. The method will return an array of key
=> value
pairs. Cache keys that do not exist or have expired will have defaultValue
as a value (default null
).
$value = $cache->getMultiple(['my-key1', 'my-key2']);
$value = $cache->getMultiple(['my-key1', 'my-key2'], 'default');
has
¶
To check whether a key exists in the cache (or it has not expired) you can call the has()
method. The method will return true
if the key exists, or false
otherwise.
set
- setMultiple
¶
To save the data in the cache, you will need to use the set()
method. The method accepts the key we wish to store the data under and the value of the item to store. The data needs to be of a type that supports serialization i.e. PHP type or an object that implements serialization. The last (optional) parameter is the TTL (time to live) value for this item. This option might not always be available, if the underlying adapter does not support it. The method will return true
if the key exists, or false
otherwise. If even one key is not successfully stored, the method will return false
.
If you wish to store more than one element with one call, you can call setMultiple()
, passing an array of key => value pairs for the multiple-set operation. As with set
the last (optional) parameter is the TTL (time to live). The method will return true
if the key exists, or false
otherwise.
delete
- deleteMultiple
- clear
¶
To delete an item from the cache you need to call the delete()
method with a key. The method returns true
on success and false
on failure. `
If you wish to delete more than one key with one call, you can call deleteMultiple()
, passing an array with the keys needed. The method returns true
on success and false
on failure. If even one key is not successfully deleted, the method will return false
. `
If you wish to clear all the keys, you can call the clear()
method. The method returns true
on success and false
on failure.
Factory¶
newInstance
¶
We can easily create a Phalcon\Cache class using the new
keyword. However Phalcon offers the Phalcon\Cache\CacheFactory class, so that developers can easily instantiate cache objects. The factory will accept a Phalcon\Cache\AdapterFactory object which will in turn be used to instantiate the necessary Cache class with the selected adapter and options. The factory always returns a new instance of Phalcon\Cache.
The example below shows how you can create a cache object using the Apcu
adapter and Json
serializer:
<?php
use Phalcon\Cache\CacheFactory;
use Phalcon\Cache\AdapterFactory;
use Phalcon\Storage\SerializerFactory;
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
];
$serializerFactory = new SerializerFactory();
$adapterFactory = new AdapterFactory(
$serializerFactory,
$options
);
$cacheFactory = new CacheFactory($adapterFactory);
$cache = $cacheFactory->newInstance('apcu');
load
¶
The Cache Factory also offers the load
method, which accepts a configuration object. This object can be an array or a Phalcon\Config object, with directives that are used to set up the cache. The object requires the adapter
element, as well as the options
element with the necessary directives.
<?php
use Phalcon\Cache\CacheFactory;
use Phalcon\Cache\AdapterFactory;
use Phalcon\Storage\SerializerFactory;
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
];
$serializerFactory = new SerializerFactory();
$adapterFactory = new AdapterFactory(
$serializerFactory,
$options
);
$cacheFactory = new CacheFactory($adapterFactory);
$cacheOptions = [
'adapter' => 'apcu',
'options' => [
'prefix' => 'my-prefix',
],
];
$cache = $cacheFactory->load($cacheOptions);
Exceptions¶
Any exceptions thrown in the Cache component will be of type Phalcon\Cache\Exception\Exception which implements Psr\SimpleCache\CacheException. Additionally the Phalcon\Cache\Exception\InvalidArgumentException which implements also the Psr\SimpleCache\CacheException. It is thrown when the data supplied to the component or any sub components is not valid. You can use these exceptions to selectively catch exceptions thrown only from this component.
<?php
use Phalcon\Cache\Exception\Exception;
use Phalcon\Mvc\Controller;
class IndexController extends Controller
{
public function index()
{
try {
// Get some configuration values
$this->cache->get('some-key');
} catch (Exception $ex) {
echo $ex->getMessage();
}
}
}
Serializers¶
The Phalcon\Storage\Serializer
namespace offers classes that implement the Serializable interface and thus expose the serialize
and unserialize
methods. The purpose of these classes is to transform the data before saving it to the storage and after retrieving it from the storage.
NOTE
The default serializer for all adapters is Phalcon\Storage\Serializer\Php
which uses PHP's serialize
and unserialize
methods. These methods can suit most applications. However the developer might want to use something more efficient such as igbinary which is faster and achieves a better compression.
The cache adapter can be configured to use a different serializer. The available serializers are:
Base64
¶
This serializer uses the base64_encode
and base64_decode
methods to serialize data. The input must be of type string
, therefore this serializer has obvious limitations
Igbinary
¶
The igbinary
serializes relies on the igbinary_serialize
and igbinary_unserialize
methods. Those methods are exposed via the igbinary PHP extension, which has to be installed and loaded on the target system.
Json
¶
The JSON
serializer uses json_encode
and json_decode
. The target system must have JSON support available for PHP.
Msgpack
¶
Similar to igbinary
the msgpack
serializer uses msgpack_pack
and msgpack_unpack
for serializing and unserializing data. This, along with igbinary
is one of the fastest and most efficient serializers. However, it requires that the msgpack PHP extension is loaded on the target system.
None
¶
This serializer does not transform the data at all. Both its serialize
and unserialize
get and set the data without altering it.
Php
¶
This is the default serializer. It uses PHP's serialize
and unserialize
methods for data transformations.
Custom¶
Phalcon also offers the Phalcon\Storage\Serializer\SerializerInterface` which can be implemented in a custom class. The class can offer the serialization you require.
<?php
namespace MyApp\Storage\Serializer;
use Phalcon\Storage\SerializerInterface;
class Garble implements SerializerInterface
{
/**
* Data storage
*
* @var string
*/
private $data = '';
/**
* Return the stored data
*
* @return string
*/
public function getData(): string
{
return $this->data;
}
/**
* Serializes data
*/
public function serialize(): string
{
return rot13($this->data);
}
/**
* Set the data
*
* @var Garble
*
* @return Garble
*/
public function setData($data): Garble
{
$this->data = (string) $data;
return $this;
}
/**
* Unserializes data
*/
public function unserialize($data): void
{
$this->data = str_rot13($data);
}
}
<?php
namespace MyApp;
use MyApp\Storage\Serializer\Garble;
$data = 'I came, I saw, I conquered.';
$garble = new Garble();
$garble
->setData($data)
->serialize()
;
echo $garble->getData(); // "V pnzr, V fnj, V pbadhrerq."
$encrypted = 'V pnzr, V fnj, V pbadhrerq.';
$garble->unserialize($encrypted);
echo $garble->getData(); // "I came, I saw, I conquered."
Serializer Factory¶
Although all serializer classes can be instantiated using the new
keyword, Phalcon offers the Phalcon\Storage\SerializerFactory class, so that developers can easily instantiate serializer classes. All the above serializers are registered in the factory and lazy loaded when called. The factory also allows you to register additional (custom) serializer classes. The only thing to consider is choosing the name of the serializer in comparison to the existing ones. If you define the same name, you will overwrite the built-in one.The objects are cached in the factory so if you call the newInstance()
method with the same parameters during the same request, you will get the same object back.
The example below shows how you can create a Json
serializer either using the new
keyword or the factory:
<?php
use Phalcon\Storage\Serializer\Json;
use Phalcon\Storage\SerializerFactory;
$jsonSerializer = new Json();
$factory = new SerializerFactory();
$jsonSerializer = $factory->newInstance('json');
base64
for Phalcon\Storage\Serializer\Base64igbinary
for Phalcon\Storage\Serializer\Igbinaryjson
for Phalcon\Storage\Serializer\Jsonmsgpack
for Phalcon\Storage\Serializer\Msgpacknone
for Phalcon\Storage\Serializer\Nonephp
for Phalcon\Storage\Serializer\Php
Adapters¶
The Phalcon\Cache\Adapter
namespace offers classes that implement the Phalcon\Cache\Adapter\AdapterInterface interface. It exposes common methods that are used to perform operations on the storage adapter or cache backend. These adapters act as wrappers to respective backend code.
The available methdods are:
Method | Description |
---|---|
clear | Flushes/clears the cache |
decrement | Decrements a stored number |
delete | Deletes data from the adapter |
get | Reads data from the adapter |
getAdapter | Returns the already connected adapter or connects to the backend server(s) |
getKeys | Returns all the keys stored (optional filter parameter) |
getPrefix | Returns the prefix for the keys |
has | Checks if an element exists in the cache |
increment | Increments a stored number |
set | Stores data in the adapter |
NOTE
The getAdapter()
method returns the connected adapter. This offers more flexibility to the developer, since it can be used to execute additional methods that each adapter offers. For instance for the Redis
adapter you can use the getAdapter()
to obtain the connected object and call zAdd
, zRange
and other methods not exposed by the Phalcon adapter.
To construct one of these objects, you will need to pass a Phalcon\Storage\SerializerFactory object in the constructor and optionally some parameters required for the adapter of your choice. The list of options is outlined below.
The available adapters are:
Apcu
¶
This adapter uses Apcu
to store the data. In order to use this adapter, you will need to have apcu enabled in your target system. This class does not use an actual adapter, since the apcu
functionality is exposed using the apcu_*
PHP functions.
Option | Default |
---|---|
defaultSerializer | Php |
lifetime | 3600 |
serializer | null |
prefix | ph-apcu- |
The following example demonstrates how to create a new Apcu
cache adapter, which will use the Phalcon\Storage\Serializer\Json serializer and have a default lifetime of 7200.
<?php
use Phalcon\Cache\Adapter\Apcu;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
];
$adapter = new Apcu($serializerFactory, $options);
The above example used a Phalcon\Storage\SerializerFactory object and the defaultSerializer
option to tell the adapter to instantiate the relevant serializer.
Libmemcached
¶
This adapter utilizes PHP's memcached extension to connect to Memcached servers. The adapter used is an instance of the Memcached
class, created after the first event that requires the connection to be active.
Option | Default |
---|---|
defaultSerializer | Php |
lifetime | 3600 |
serializer | null |
prefix | ph-memc- |
servers[0]['host'] | 127.0.0.1 |
servers[0]['port'] | 11211 |
servers[0]['weight'] | 1 |
persistentId | ph-mcid- |
saslAuthData['user'] | |
saslAuthData['pass'] | |
client[\Memcached::OPT_CONNECT_TIMEOUT] | 10 |
client[\Memcached::OPT_DISTRIBUTION] | \Memcached::DISTRIBUTION_CONSISTENT |
client[\Memcached::OPT_SERVER_FAILURE_LIMIT] | 2 |
client[\Memcached::OPT_REMOVE_FAILED_SERVERS] | true |
client[\Memcached::OPT_RETRY_TIMEOUT] | 1 |
You can specify more than one server in the options array passed in the constructor. If SASL
data is defined, the adapter will try to authenticate using the passed data. If there is an error in the options or the class cannot add one or more servers in the pool, a Phalcon\Storage\Exception
will be thrown.
The following example demonstrates how to create a new Libmemcached
cache adapter, which will use the Phalcon\Storage\Serializer\Json serializer and have a default lifetime of 7200. It will use the 10.4.13.100
as the first server with weight 1
connecting to port 11211
and 10.4.13.110
as the second server with weight 5
again connecting to port 11211
.
<?php
use Phalcon\Cache\Adapter\Libmemcached;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
'servers' => [
0 => [
'host' => '10.4.13.100',
'port' => 11211,
'weight' => 1,
],
1 => [
'host' => '10.4.13.110',
'port' => 11211,
'weight' => 5,
],
],
];
$adapter = new Libmemcached($serializerFactory, $options);
The above example used a Phalcon\Storage\SerializerFactory object and the defaultSerializer
option to tell the adapter to instantiate the relevant serializer.
Serializers: The Memcached
class which is the adapter that the Phalcon\Cache\Adapter\Libmemcached uses, offers support for serializing out of the box. The built-in serializers are:
\Memcached::SERIALIZER_PHP
\Memcached::SERIALIZER_JSON
\Memcached::SERIALIZER_IGBINARY
The igbinary built-in serializer is only available if igbinary
is present in the target system and Memcached extension is compiled with it.
NOTE
If the defaultSerializer
or the selected serializer for Libmemcached
is supported as a built-in serializer (PHP
, JSON
, IGBINARY
), the built-in one will be used, resulting in more speed and less resource utilization.
Memory
¶
This adapter uses the computer's memory to store the data. As all data is stored in memory, there is no persistence, meaning that once the request is completed, the data is lost. This adapter can be used for testing or temporary storage during a particular request. The options available for the constructor are:
Option | Default |
---|---|
defaultSerializer | Php |
lifetime | 3600 |
serializer | null |
prefix | ph-memo- |
The following example demonstrates how to create a new Memory
cache adapter, which will use the Phalcon\Storage\Serializer\Json serializer and have a default lifetime of 7200.
<?php
use Phalcon\Cache\Adapter\Memory;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
];
$adapter = new Memory($serializerFactory, $options);
The above example used a Phalcon\Storage\SerializerFactory object and the defaultSerializer
option to tell the adapter to instantiate the relevant serializer.
Redis
¶
This adapter utilizes PHP's redis extension to connect to a Redis server. The adapter used is an instance of the Redis
class, created after the first event that requires the connection to be active.
Option | Default |
---|---|
defaultSerializer | Php |
lifetime | 3600 |
serializer | null |
prefix | ph-reds- |
host | 127.0.0.1 |
port | 6379 |
index | 1 |
persistent | false |
auth | |
socket |
If auth
data is defined, the adapter will try to authenticate using the passed data. If there is an error in the options, or the server cannot connect or authenticate, a Phalcon\Storage\Exception
will be thrown.
The following example demonstrates how to create a new Redis
cache adapter, which will use the Phalcon\Storage\Serializer\Json serializer and have a default lifetime of 7200. It will use the 10.4.13.100
as the host, connect to port 6379
and select the index 1
.
<?php
use Phalcon\Cache\Adapter\Redis;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
'host' => '10.4.13.100',
'port' => 6379,
'index' => 1,
];
$adapter = new Redis($serializerFactory, $options);
The above example used a Phalcon\Storage\SerializerFactory object and the defaultSerializer
option to tell the adapter to instantiate the relevant serializer.
Serializers: The Redis
class which is the adapter that the Phalcon\Cache\Adapter\Redis uses, offers support for serializing out of the box. The built-in serializers are:
\Redis::SERIALIZER_NONE
\Redis::SERIALIZER_PHP
\Redis::SERIALIZER_IGBINARY
\Redis::SERIALIZER_MSGPACK
The igbinary and built-in serializer is only available if igbinary
is present in the target system and Redis extension is compiled with it. The same applies to msgpack built-in serializer. It is only available if msgpack
is present in the target system and the Redis extension is compiled with it.
NOTE
If the defaultSerializer
or the selected serializer for Redis
is supported as a built-in serializer (NONE
, PHP
, IGBINARY
, MSGPACK
), the built-in one will be used, resulting in more speed and less resource utilization.
increment
- decrement
At this point in time there is an issue with Redis
, where the internal Redis
serializer does not skip scalar values because it can only store strings. As a result, if you use increment
after a set
for a number, will not return a number:
The way to store numbers and use the increment
(or decrement
) is to either remove the internal serializer for Redis
or you could use increment
instead of using set
at the first setting of the value to the key:
$cache->delete('my-key');
$cache->increment('my-key', 2);
echo $cache->get('my-key'); // 2
$cache->increment('my-key', 3);
echo $cache->get('my-key'); // 3
Stream
¶
This adapter is the simplest to setup since it uses the target system's file system (it only requires a cache path that is writeable). It is one of the slowest cache adapters since the data has to be written to the file system. Each file created corresponds to a key stored. The file contains additional metadata to calculate the lifetime of the cache element, resulting in additional reads and writes to the file system.
Option | Default |
---|---|
defaultSerializer | Php |
lifetime | 3600 |
serializer | null |
prefix | phstrm- |
storageDir |
If the storageDir
is not defined a Phalcon\Storage\Exception
will be thrown.
NOTE
The adapter utilizes logic to store files in separate sub directories based on the name of the key passed, thus avoiding the too many files in one folder
limit present in Windows or Linux based systems.
The following example demonstrates how to create a new Stream
cache adapter, which will use the Phalcon\Storage\Serializer\Json serializer and have a default lifetime of 7200. It will store the cached data in /data/storage/cache
.
<?php
use Phalcon\Cache\Adapter\Stream;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
'storageDir' => '/data/storage/cache',
];
$adapter = new Stream($serializerFactory, $options);
The above example used a Phalcon\Storage\SerializerFactory object and the defaultSerializer
option to tell the adapter to instantiate the relevant serializer.
Custom¶
Phalcon also offers the Phalcon\Cache\Adapter\AdapterInterface which can be implemented in a custom class. The class can offer the cache adapter functionality you require.
<?php
namespace MyApp\Cache\Adapter;
use Phalcon\Cache\Adapter\AdapterInterface;
class Custom implements AdapterInterface
{
/**
* Flushes/clears the cache
*/
public function clear(): bool
{
// Custom implementation
}
/**
* Decrements a stored number
*/
public function decrement(string $key, int $value = 1)
{
// Custom implementation
}
/**
* Deletes data from the adapter
*/
public function delete(string $key): bool
{
// Custom implementation
}
/**
* Reads data from the adapter
*/
public function get(string $key)
{
// Custom implementation
}
/**
* Returns the already connected adapter or connects to the backend
* server(s)
*/
public function getAdapter()
{
// Custom implementation
}
/**
* Returns all the keys stored. If a filter has been passed the keys that
* match the filter will be returned
*/
public function getKeys(string $prefix = ""): array
{
// Custom implementation
}
/**
* Returns the prefix for the keys
*/
public function getPrefix(): string
{
// Custom implementation
}
/**
* Checks if an element exists in the cache
*/
public function has(string $key): bool
{
// Custom implementation
}
/**
* Increments a stored number
*/
public function increment(string $key, int $value = 1)
{
// Custom implementation
}
/**
* Stores data in the adapter
*/
public function set(string $key, $value, $ttl = null): bool
{
// Custom implementation
}
}
<?php
namespace MyApp;
use MyApp\Cache\Adapter\Custom;
$custom = new Custom();
$custom->set('my-key', $data);
Adapter Factory¶
Although all adapter classes can be instantiated using the new
keyword, Phalcon offers the Phalcon\Cache\AdapterFactory class, so that you can easily instantiate cache adapter classes. All the above adapters are registered in the factory and lazy loaded when called. The factory also allows you to register additional (custom) adapter classes. The only thing to consider is choosing the name of the adapter in comparison to the existing ones. If you define the same name, you will overwrite the built-in one. The objects are cached in the factory so if you call the newInstance()
method with the same parameters during the same request, you will get the same object back.
The example below shows how you can create a Apcu
cache adapter with the new
keyword or the factory:
<?php
use Phalcon\Cache\Adapter\Apcu;
use Phalcon\Storage\Serializer\Json;
$jsonSerializer = new Json();
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
'serializer' => $jsonSerializer,
];
$adapter = new Apcu(null, $options);
<?php
use Phalcon\Cache\AdapterFactory;
use Phalcon\Storage\SerializerFactory;
$serializerFactory = new SerializerFactory();
$adapterFactory = new AdapterFactory($serializerFactory);
$options = [
'defaultSerializer' => 'Json',
'lifetime' => 7200,
];
$adapter = $adapterFactory->newInstance('apcu', $options);
The parameters you can use for the factory are: * apcu
for Phalcon\Cache\Adapter\Apcu
* libmemcached
for Phalcon\Cache\Adapter\Libmemcached * memory
for Phalcon\Cache\Adapter\Memory
* redis
for Phalcon\Cache\Adapter\Redis
* stream
for Phalcon\Cache\Adapter\Stream