Skip to content
Snippets Groups Projects
Unverified Commit fe5ebe8d authored by totten's avatar totten Committed by GitHub
Browse files

Merge pull request #544 from totten/master-cache

Cache Reference - Link to admin guide. Add section on custom caches.
parents f65d88e9 c67a1df7
No related branches found
No related tags found
No related merge requests found
# Cache Reference
## Using the cache
## Using the default cache {:#default}
`Civi::cache()` is the simplest way to access the cache, automatically using the default cache type (described below). The `CRM_Utils_Cache_Interface` class lays out the methods for saving and retrieving cached items.
`Civi::cache()` is the simplest way to access the cache, automatically using the default cache type (described in [Configuration](#configuration)).
### Methods
The `CRM_Utils_Cache_Interface` class lays out the methods for saving and retrieving cached items.
* Set a cache value
```php
......@@ -30,6 +32,13 @@
Civi::cache()->flush();
```
!!! tip "PSR-16 Compliance (v5.4+)"
In CiviCRM v5.4+, the cache complies with the PHP-FIG standard [PSR-16](https://www.php-fig.org/psr/psr-16/). `CRM_Utils_Cache_Interface` extends the simple `CacheInterface`, although the implementations differ in a couple small ways:
* The flush function has two names -- `clear()` (per PSR-16) and `flush()` (which predates PSR-16). These are synonyms.
* Cache keys in PSR-16 are prohibited from using certain characters. However, some of these characters were supported in previous versions of CiviCRM's cache interface. To enable a transition, these restrictions are *not* enforced in a default runtime. However, they *are* enforced during testing, and they can be enabled in `civicrm.settings.php` by toggling `CIVICRM_PSR16_STRICT`.
### Example
```php
......@@ -49,16 +58,108 @@ function findMagicNumber() {
}
```
## Cache types
### Configuration
In a stock configuration, the `Civi::cache()` object stores data in a local PHP variable (`ArrayCache`). This allows frequent, high-speed I/O, but
it only retains data for the scope of one page-request -- which reduces the potential performance gains.
System administrators may configure the default cache to use a more long-term backend, such as `Memcached` or `Redis`. For more information about
configuring the default cache driver, see [System Administrator Guide => Setup => Caches](https://docs.civicrm.org/sysadmin/en/latest/setup/cache/).
### Aliases
In reading code, you may find these three notations -- which all refer to the same thing:
* `Civi::cache()`
* `Civi::cache('default')`
* `CRM_Utils_Cache::singleton()`
## Using a custom cache {:#custom}
This is selected in `civicrm.settings.php`, where `CIVICRM_DB_CACHE_CLASS` is defined.
Generally, it's best to store caches in a memory-backed service like Redis or Memcached. But what happens if the system-configuration doesn't support that?
Perhaps you store the cache in a MySQL table? Or a data-file? Or a PHP array?
* "ArrayCache" - This is the default, using an in-memory array.
The answers should not be the same for all data. For example:
* "Memcache" - This is for the PHP Memcache extension.
* If the cache is tracking metadata derived from `civicrm_option_value`, then you can get the original data pretty quickly (by querying MySQL).
Writing the cache to another MySQL table or data-file would serve little benefit.
* If the cache is tracking a remote feed (fetched from another continent via HTTPS), then it's much more expensive to get the original data. In absence of
Redis/Memcached, you might put the cache in a MySQL table or a data-file.
* "Memcached" - This is for the PHP Memcached extension.
With a *custom cache object*, a developer gets the same interface (`CRM_Utils_Cache_Interface` / PSR-16), but they can define different preferences
for how to store the cache-data. In particular, you can define a fallback list. Compare these examples:
* "APCcache" - This is for the PHP APC extension.
* `['SqlGroup', 'ArrayCache']` means "If MySQL is available, use that. Otherwise, use a local PHP array."
* `['*memory*', 'SqlGroup', 'ArrayCache']` means "If any memory service is available, then use that. Otherwise, if MySQL is available, use that. As a last resort, use a local PHP array."
* "NoCache" - This caches nothing
You can manually instantiate a custom cache object using the factory-function, `CRM_Utils_Cache::create()`. This is good for demonstrating the concept and for some edge-cases. However,
in typical usage, it's more common to register a named service.
### Example: Manual
```php
// Create a cache object
$cache = CRM_Utils_Cache::create([
'type' => ['SqlGroup', 'ArrayCache'],
'name' => 'HelloWorld',
]);
```
A few things to notice here:
* The `type` parameter is an array of preferred storage systems.
* The `name` will be passed down to the storage system -- ensuring that different caches are stored separately.
* Ex: In `Memcached`/`Redis`, the `name` becomes part of the cache-key.
* Ex: In `SqlGroup`, the `name` corresponds to the field `civicrm_cache.group_name`.
Once you have the `$cache` object, it supports all the methods of `CRM_Utils_Cache_Interface` and PSR-16.
```php
// Use the cache object
$value = $cache->get('name');
if ($value === NULL) {
$cache->set('name', 'Alice');
}
// Flush the contents of the cache
$cache->flush();
```
### Example: Named service
First, we define the service in `Civi\Core\Container` or `hook_civicrm_container`:
```php
$container->setDefinition("cache.hello", new Definition(
'CRM_Utils_Cache_Interface',
[[
'type' => ['*memory*', 'SqlGroup', 'ArrayCache'],
'name' => 'HelloWorld',
]]
))->setFactory('CRM_Utils_Cache::create');
```
As before, notice that:
* The `type` parameter is an array of preferred storage systems. It will choose the first valid driver.
* The `name` will be passed down to the storage system.
* The service is an instance of `CRM_Utils_Cache_Interface` (PSR-16).
Once the service is declared, we can get a reference to the cache in several ways:
* Lookup the service with `Civi::cache('hello')`
* Lookup the service with `Civi::service('cache.hello')`
* Inject the service using the container's dependency-injection
For example, we could use `Civi::cache('hello')` as follows:
```php
// Use the cache object
$value = Civi::cache('hello')->get('name');
if ($value === NULL) {
Civi::cache('hello')->set('name', 'Alice');
}
// Flush the contents of the cache
Civi::cache('hello')->flush();
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment