Translations
Info
All page names need to be in English.
en da  de  fr  it  ja  km  nl  ru  zh

Caching framework

From TYPO3Wiki
Jump to: navigation, search

warning - Message

This document was transferred to the official TYPO3 CMS documentation, see https://docs.typo3.org/typo3cms/CoreApiReference/CachingFramework/Index.html for details. This wiki page is now obsolete and will not get any additional updates
This page belongs to the Core Team (category Core Team)

Introduction

Since TYPO3 4.3, the core contains a data caching framework which supports a wide variety of storage solutions and options for different caching needs. Each cache can be configured individually and can implement its own specific storage strategy. Major parts of the system are backported from FLOW3 and are kept in sync between the two systems.

The caching framework exists to help speeding up TYPO3 sites, especially heavily loaded ones. It is possible to move all caches to a dedicated cache server with specialized cache systems like the Redis key-value store (a so called NoSQL database).

Before TYPO3 4.6, the caching framework was an alternative cache solution that coexisted with the old core cache mechanism. It could be enabled with a setting in localconf.php, but was not enabled by default for core caches. Even though the TYPO3 core did not use the caching framework, extensions were free to do so. An example can be found in extbase version 1.3, which uses the caching framework for PHP reflection data. Since TYPO3 4.6, the caching framework is always enabled and configuration and usage is simplified. This wiki page documents settings and differences for all supported TYPO3 versions.

Quick start for Integrators

Enable caching framework in TYPO3 4.5 and below

To enable the caching framework for core caches, the install tool option useCachingFramework must be enabled. This translates to the localconf.php setting $TYPO3_CONF_VARS['SYS']['useCachingFramework'] = true. All caches should be cleared before to make sure that the cache tables do not contain old and unused data. This setting is obsolete in TYPO3 4.6; it triggers a deprecation warning and should be removed when upgrading.

In TYPO3 4.5 and below, the default database backend requires a manual table setup that can be created in the install tool.

Change specific cache options

By default, most core caches use the database backend. Default cache configurations are defined in t3lib/config_default.php (TYPO3 4.x) / typo3/sysext/core/Configuration/DefaultConfiguration.php (TYPO3 6.x) and can be overridden in localconf.php.

If, for example, the pages cache grows very large, data compression for the pages cache with the database backend can be enabled in localconf.php:

PHP script:
if (!is_array($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_pages']['options'])) {
    $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_pages']['options'] = array();
}
$TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_pages']['options']['compression'] = true;

Cache configurations in localconf.php should not overwrite the whole configuration array, see below for details.

Possible upgrade issues

If updating TYPO3 to version 4.6 or above it might happen that a fatal php error is thrown:

Fatal error: Uncaught exception 't3lib_cache_exception_NoSuchCache' with message 'A cache with identifier "cache_phpcode" does not exist.'

This happens if localconf.php (or some ext_localconf.php file of some extension) contains a line that resets the whole cache configuration with a line like '$TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations'] = array(...)'. This was recommended by some blog posts but is highly discouraged and must be fixed: Always change specific configuration options only and never the whole sub array. The core must have a chance to come with a sane default configuration and to set up new configuration with new versions.

Garbage collection task

It is advisable to activate the garbage collection task in the scheduler, especially if using the database backend. This helps to keep caches clean and small.

InnoDB issues

The database backend for MySQL uses InnoDB tables. Due to the nature of InnoDB, deleting records does not reclaim the actual disk space. E.g. if the cache uses 10GB, cleaning it will still keep 10GB allocated on the disk even though phpMyAdmin will show 0 as the cache table size. To reclaim the space, turn on the MySQL option file_per_table, drop the cache tables and re-create them using the Install tool.

This does not by any mean that you should skip the scheduler task. Deleting records still improves performance.

Configuration

Caches are configured in the array $TYPO3_CONF_VARS['SYS']['caching']. The basic structure is predefined in t3lib/config_default.php and consists of the section:

  • cacheConfigurations: Registry of all configured caches, used frontends, backends and backend options.

In TYPO3 4.5 and below two other sub arrays exist to register available frontends and backends. In TYPO3 4.6 the autoloader for additional frontend and backend classes must be used which rendered this registry obsolete:

  • cacheFrontends: Registry of all available frontends (Removed, obsolete since 4.6)
  • cacheBackends: Registry of all available backends (Removed, obsolete since 4.6)

Cache configurations

Some backends have mandatory as well as optional parameters (which are documented below). If not all mandatory options are defined, the backend will throw an exception on the first access.

In TYPO3 4.5 and below the settings backend and frontend are mandatory, with TYPO3 4.6 a fallback to the VariableFrontend and DbBackend is implemented.

Example configuration in localconf.php to switch the pages cache to the redis backend:

PHP script:
$TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_pages']['backend'] = 't3lib_cache_backend_RedisBackend';
$TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_pages']['options'] = array(
    'database' => 3,
);

How to disable specific caches

During development, it can be convenient to disable certain caches. This is especially helpful since TYPO3 4.6 for central caches like the language or autoloader cache. This can be achieved by using the null backend (see below) as storage backend.

Warning: Do not use this in production, it will slow down the system! Example entry in localconf.php to switch the phpcode cache (used for the autoloader cache) to use the null backend:

PHP script:
$TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['cache_phpcode']['backend'] = 't3lib_cache_backend_NullBackend';

Used core caches

notice - Note

This section should be completed if current core cache definitions are settled for TYPO3 4.6

notice - Note

This section is only valid for TYPO3 4.6 and above

The TYPO3 core defines and uses several caching framework caches by default. This section gives an overview of default caches, its usage and behaviour:

  • cache_phpcode
    • This cache uses PhpFrontend and can be used to store compiled PHP code that can be require()'d directly.
    • The core autoloader uses this cache to store a register of available classes:
      • The autoloader cache file builds the cache identifier from the current TYPO3 version and if the current request is a frontend or a backend request. Typically, there will be two cache files: One for frontend and one for backend.
      • The autoloader cache files are optimized for production use and only rebuild when upgrading TYPO3 or if installing a new extension.
      • While developing, trying to use new classes that are not yet in the class index can result in fatal errors. In this case, 'Clear all cache' in the backend must be used; this forces the system to repopulate the class index. In rare cases, for example when classes that are required during TYPO3 bootstrap are introduced (usually when working on the TYPO3 core), the 'Clear all cache' request itself might throw a fatal error. The solution here is to manually remove the cache files from typo3temp/Cache/Code/cache_phpcode is required. If this happens frequently during developing, it might be easier to configure the 'NullBackend' for this cache.

Garbage collection task

TYPO3 4.5 and above provide a task to garbage collect cache backends. This is important for backends like the database backend that do not remove old cache entries and tags internally. It is highly recommended to add this scheduler task and run it once in a while (maybe once a day at night) for all used backends which do not delete entries which exceeded their lifetime on their own to free up memory or hard disk space.

Basic cache knowhow

The caching framework can handle multiple caches with different configurations. A single cache consists of any number of cache entries.

A single cache entry is defined by these fields:

  • identifier: A string as unique identifier within this cache. Used to store and retrieve entries.
  • data: The data to be cached.
  • lifetime: A lifetime in seconds of this cache entry. The entry can not be retrieved from cache if lifetime expired.
  • tags: Additional tags (an array of strings) assigned to the entry. Used to remove specific cache entries.

Identifier and tags

The difference between identifier and tags is quite simple: an identifier uniquely identifies a cache entry, and a tag is additional data applied to an entry (used for cache eviction). Thus, an identifier refers to a single cache entry, and a tag can refer to multiple cache entries.

An example:

About the identifier

The identifier is used to store ("set") and retrieve ("get") entries from the cache and holds all information to differentiate entries from each other. For performance reasons, it should be quick to calculate.

Suppose there is an resource-intensive extension added as a plugin on two different pages. The calculated content depends on the page on which it is inserted and if a user is logged in or not. So, the plugin creates at maximum four different content outputs, which can be cached in four different cache entries:

  • page 1, no user logged in
  • page 1, a user is logged in
  • page 2, no user logged in
  • page 2, a user is logged in

To differentiate all entries from each other, the identifier is built from the page ID where the plugin is located, combined with the information whether a user is logged in. These are concatenated and hashed (with sha1(), for example). In PHP this could look like this:

PHP script:
$identifier = sha1((string)$this->getPageUid() . (string)$this->isUserLoggedIn());

notice - Note

sha1 is a good hash algorithm in this case, collisions are extremely unlikely. It scales O(n) with the input length.

When the plugin is accessed, the identifier is calculated early in the program flow. Next, the plugin looks up for a cache entry with this identifier. If there is such an entry, the plugin can return the cached content, else it calculates the content and stores a new cache entry with this identifier. In general the identifier is constructed from all dependencies which specify an unique set of data. The identifier should be based on information which already exist in the system at the point of its calculation. In the above scenario the page id and whether or not a user is logged in are already determined during the frontend bootstrap and can be retrieved from the system quickly.

About tags

Tags are used to drop specific cache entries if the information an entry is constructed from changes.

Suppose the above plugin displays content based on different news entries. If one news entry is changed in the backend, all cache entries which are compiled from this news row must be dropped to ensure that the frontend renders the plugin content again and does not deliver old content on the next frontend call. If for example the plugin uses news number one and two on one page, and news one on another page, the according cache entries should be tagged with these tags:

  • page 1, tags news_1, news_2
  • page 2, tag news_1

If entry two is changed, a simple backend logic (probably a hook in TCEMAIN) could be created, which drops all cache entries tagged with 'news_2', in this case the first entry would be invalidated while the second entry still exists in the cache after the operation. While there is always exactly one identifier for each cache entry, an arbitrary number of tags can be assigned to an entry and one specific tag can be assigned to mulitple cache entries. All tags a cache entry has are given to the cache when the entry is stored (set).

System architecture

The caching framework architecture is based on these classes:

  • t3lib_cache: Adapter class between FLOW3 cache logic and TYPO3 v4 core implementation. Used by core and extensions to initialize the framework. Creates instances of t3lib_cache_factory and t3lib_cache_manager.
  • t3lib_cache_factory: Factory class to instantiate cache manager and caches.
  • t3lib_cache_manager: Returns the cache frontend of a specific cache. Implements methods to handle cache instances.
  • t3lib_cache_frontend_Frontend: Main interface to handle cache entries of a specific cache. Different frontends exist to handle different data types.
  • t3lib_cache_backend_Backend: Interface for different storage strategies. A set of implementations exist with different characteristics.

Cache frontends

Available frontends

Currenly three different frontends are implemented, the main difference is the data types which can be stored using a specific frontend.

t3lib_cache_frontend_StringFrontend

The string frontend accepts strings as data to be cached.

t3lib_cache_frontend_VariableFrontend

Strings, arrays and objects are accepted by this frontend. Data is serialized before it is passed to the backend. Since version 4.5, the igbinary serializer is used transparently (if available in the system) which speeds up the serialization and unserialization and reduces data size. The variable frontend is the most frequently used frontend and handles the widest range of data types. While it can also handle string data, the string frontend should be used if the cache needs to store strings only to avoid the additional serialization done by the variable frontend.

t3lib_cache_frontend_PhpFrontend

This is a special frontend to cache PHP files. It extends the string frontend with the method requireOnce() and allows PHP files to be require()'d if a cache entry exists. This can be used by extensions to cache and speed up loading of calculated PHP code and becomes handy if a lot of reflection and dynamic PHP class construction is done. A backend to be used with the PHP frontend must implement the interface t3lib_cache_backend_PhpCapableBackend. Currently the file backend is the only backend which fulfills this requirement.

notice - Note

The PHP frontend can only be used to cache PHP files, it does not work with strings, arrays or objects. It is not intended as a page content cache.

Frontend API

All frontends must implement the API defined in the interface t3lib_cache_frontend. All operations on a specific cache must be done with these methods.

  • getIdentifier() Returns the cache identifier.
  • getBackend() Returns the backend instance of this cache. It is seldom needed in usual code.
  • set() Sets/overwrites an entry in the cache.
  • get() Return the cache entry for the given identifier.
  • has() Check for existance of a cache entry. Do no use this prior to get() since get() returns NULL if an entry does not exist.
  • remove() Remove the entry for the given identifier from the cache.
  • flushByTag() Flush all cache entries which are tagged with the given tag.
  • flushByTags() Remove all cache entries which are tagged with one of the given tags (deprecated since TYPO3 4.6)
  • collectGarbage() Call the garbage collection method of the backend. This is important for backends which are unable to do this internally (like the DB backend).
  • isValidEntryIdentifier() Checks if a given identifier is valid.
  • isValidTag() Checks if a given tag is valid.
  • requireOnce() PhpFrontend only: Used to require a cached php file directly.

Cache backends

Currently already a number of different storage backends exists. They have different characteristics and can be used for different caching needs. The best backend depends on given server setup and hardware, as well as cache type and usage. A backend should be chosen wisely, a wrong decision could slow down a TYPO3 installation in the end.

Common options

Option Description Mandatory Type Default
defaultLifetime Default lifetime in seconds of a cache entry if it is not specified for a specific entry on set() No integer 3600

t3lib_cache_backend_DbBackend

This is the main backend suitable for most storage needs. It does not require additional server daemons or server configuration.

The database backend does no automatic garbage collection, the garbage collection task should be used to remove old tag and data entries which exceeded their lifetime.

It stores data in the configured database (usually MySQL) and can handle large amounts of data with reasonable performance. Data and tags are stored in two different tables, every cache needs its own set of tables. Performancewise the database backend is already optimized pretty well and should be used as default backend if in doubt.

In TYPO3 4.5 and below the needed table structure had to be defined in ext_tables.sql and should be cloned from given core cache tables like 'cachingframework_cache_pages' and 'cachingframework_cache_pages_tags'. The options cacheTable and tagsTable are mandatory. With TYPO3 4.6 the database backend takes care of needed table structure on its own and the cacheTable and tagsTable options are ignored.

For caches with a lot of read and write operations it is important to tune the MySQL setup. The most important setting is the innodb_buffer_pool_size, a generic goal is to give MySQL as much RAM as needed to have the main table space loaded completely in memory.

The database backend tends to slow down if there are many write operations and big caches which do not fit into memory because of slow harddrive seek and write performance. If the data table grows too big to fit into memory, it is possible to compress given data transparently with this backend; which often shrinks the amount of needed space to 1/4 or less. The overhead of the compress/uncrompress operation is usually not high. A good candidate for a cache with enabled compression is the core pages cache: It is only read or written once per request and the data size is pretty large. The compression should not be enabled for caches which are read or written very often during one request.

Note: Next to the memory given to InnoDB, a TYPO3 system with heavy use of the database backend can easily reach the maximum write and seek performance of the underlying storage system (hard disk), especially with high set() load. There are several parameters for MySQL InnoDB like innodb_flush_log_at_trx_commit which can influence the MySQL persistence strategy at the cost of data integrity in case of server outage.

Options

Option Description Mandatory Type Default
cacheTable Data table of this cache. Every cache needs to have its own table. Obsolete since TYPO3 4.6. Yes (only TYPO3 4.5 and below) string
tagsTable Tags table of this cache. Every cache needs to have its own table. Obsolete since TYPO3 4.6. Yes (only TYPO3 4.5 and below) string
compression Whether or not data should be compressed with gzip. This can reduce size of the cache data table, but incurs CPU overhead for compression and decompression. No boolean false
compressionLevel gzip compression level (if the compression option is set to true). The default compression level is usually sufficient.
  • -1: Default gzip compression (recommended)
  • 0: No compression
  • 9: Maximum compression (costs a lot of CPU)
No integer from -1 to 9 -1

t3lib_cache_backend_MemcachedBackend

Memcached is a simple, distributed key/value RAM database. To use this backend, at least one memcached daemon must be reachable, and the PECL module "memcache" must be loaded. There are two PHP memcached implementations: "memcache" and "memcached". Currently, only memcache is supported by this backend.

Warning and design constraints

Memcached is by design a simple key-value store. Values must be strings and there is no relation between keys. Since the caching framework needs to structure it to store the identifier-data-tags relations, for each cache entry it stores an identifier->data, identifier->tags and a tag->identifiers entry.

This leads to structural problems:

  • If memcache runs out of memory but must store new entries, it will toss *some* other entry out of the cache (this is called an eviction in memcached speak).
  • If data is shared over multiple memcache servers and some server fails, key/value pairs on this system will just vanish from cache.

Both cases lead to corrupted caches: If, for example, a tags->identifier entry is lost, dropByTag() will not be able to find the corresponding identifier->data entries which should be removed and they will not be deleted. This results in old data delivered by the cache. Additionally, there is currently no implementation of the garbage collection that could rebuild cache integrity.

It is important to monitor a memcached system for evictions and server outages and to clear clear caches if that happens.

Furthermore memcache has no sort of namespacing. To distinguish entries of multiple caches from each other, every entry is prefixed with the cache name. This can lead to very long runtimes if a big cache needs to be flushed, because every entry has to be handled separately and it is not possible to just truncate the whole cache with one call as this would clear the whole memcached data which might even hold non TYPO3 related entries.

Because of the mentioned drawbacks, the memcached backend should be used with care or in situations where cache integrity is not important or if a cache has no need to use tags at all.

Note: The current native debian squeeze package php5-memcache (probably other distributions are affected, too) suffers from PHP memcache bug #16927. Solution: You can download a fixed php5-memcache package for Debian Squeeze amd64 which is provided as-is.

Note: Since memcached has no sort of namespacing and access control, this backend should not be used if other third party systems do have access to the same memcached daemon for security reasons. This is a typical problem in cloud deployments where access to memcache is cheap (but could be read by third parties) and access to databases is expensive.

Options

Option Description Mandatory Type Default
servers Array of used memcached servers, at least one server must be defined. Each server definition is a string, allowed syntaxes:
  • hostname or IP: TCP connect to host on memcached default port (usually 11211, defined by PHP ini variable memcache.default_port)
  • hostname:port: TCP connect to host on port
  • tcp://hostname:port: Same as above
  • unix:///path/to/memcached.sock: Connect to memcached server using unix sockets
Yes array
compression Enable memcached internal data compression. Can be used to reduce memcached memory consumption but adds additional compression / decompression CPU overhead on the according memcached servers. No boolean false

t3lib_cache_backend_RedisBackend

Redis is a key-value storage/database. In contrast to memcached, it allows structured values. Data is stored in RAM but it allows persistence to disk and doesn't suffer from the design problems which exist with the memcached backend implementation. The redis backend can be used as an alternative of the database backend for big cache tables and helps to reduce load on database servers this way. The implementation can handle millions of cache entries each with hundreds of tags if the underlying server has enough memory.

Redis is known to be extremely fast but very memory hungry. The implementation is an option for big caches with lots of data because most important operations perform O(1) in proportion to the number of (redis) keys. This basically means that the access to an entry in a cache with a million entries is not slower than to a cache with only 10 entries, at least if there is enough memory available to hold the complete set in memory. At the moment only one redis server can be used at a time per cache, but one redis instance can handle multiple caches without performance loss when flushing a single cache.

The garbage collection task should be run once in a while to find and delete old tags.

The implementation is based on the PHP phpredis module, which must be available on the system.

Note: It is important to monitor the redis server and tune its settings to the specific caching needs and hardware capabilities. There are several articles on the net and the redis configuration file contains some important hints on how to speed up the system if it reaches bounds. A full documentation of available options is far beyond this documentation.

Warning: The redis implementation is pretty young and should be considered as experimental. The redis project itself has a very high development speed and it might happen that the TYPO3 implementation changes to adapt to new versions.

Options

Option Description Mandatory Type Default
hostname IP address or name of redis server to connect to. No string 127.0.0.1
port Port of the Redis daemon No integer 6379
database Number of the database to store entries. Each cache should use its own database, otherwise all caches sharing a database are flushed if the flush operation is issued to one of them. Database numbers 0 and 1 are used and flushed by the core unit tests and should not be used if possible. No integer 0
password Password used to connect to the redis instance if the redis server needs authentication. Warning: The password is sent to the redis server in plain text. No string
compression Wether or not data compression with gzip should be enabled. This can reduce cache size, but adds additional CPU overhead for the compress and decompress operation in PHP. No boolean false
compressionLevel Set gzip compression level to a specific value. The default compression level is usually sufficient.
  • -1: Default gzip compression (recommended)
  • 0: No compression
  • 9: Maximum compression (but more CPU overhead)
No integer from -1 to 9 -1

t3lib_cache_backend_ApcBackend

APC is mostly known as an opcode cache for PHP source files but can be used to store user data in shared memory as well. As main advantage the data can be shared between different PHP processes and requests. All calls directly access shared memory. This makes this backend lightning fast for get() and set() operations. It can be an option for relatively small caches (few dozens of megabytes) which are read and written very often and becomes handy if APC is used as opcode cache anyway.

The implementation is very similar to the memcached backend implementation and suffers from the same problems if APC runs out of memory. Garbage collection is currently not implemented. In its latest version, apc will fail to store data with a PHP warning if it runs out of memory. This may change in the future. Even without using the cache backend, it is adviseable to increase the memory cache size of apc to at least 64MB when working with TYPO3, simply due to the large number of PHP files to be cached. A minimum of 128MB is recommended when using the additional content cache. Cache TTL for file and user data should be set to zero (disabled) to avoid heavy memory fragmentation.

Note: The native Debian Lenny packages (PHP 5.2.6) have shown major memory leaks with the APC backend for unknown reasons. In the PHP 5.3 packages, this could not be reproduced.

Note: It is not advisable to use the APC backend in shared hosting environments for security reasons: The user cache in APC is not aware of different virtual hosts. Basically every PHP script which is executed on the system can read and write any data to this shared cache, given data is not encapsulated or namespaced in any way. Only use the APC backend in environments which are completely under your control and where no third party can read or tamper your data.

t3lib_cache_backend_FileBackend

The file backend stores every cache entry as a single file to the file system. The lifetime and tags are added after the data part in the same file.

As main advantage the file backend is the only backend which implements the PhpCapable interface and can be used in combination with the PhpFrontend. The backend was specifically adapted to these needs and has low overhead for get() and set() operations. It scales very well with the number of entries for the get() and set() operations. This mostly depends on the file lookup performance of the underlying file system in large directories, and most modern file systems use B-trees which can easily handle millions of files without much performance impact.

A disadvantage is that the performance of flushByTag() is bad and scales just O(n). This basically means that with twice the number of entries the file backend needs double time to remove entries which are tagged with a given tag. This practically renders the file backend unusable for content caches. The reason for this design decision in FLOW3 is that the file backend is mainly used as AOP cache, where flushByTag() is only used if a PHP file changes. This happens very seldom on production systems, so get() and set() is much more important in this scenario.

Note: The storage strategy in the file backend changed between TYPO3 4.3 and 4.4: In 4.3 the tags where stored as own files, but are interweaved with the data file since 4.4. This speeds up get() and set() a lot, but makes flushByTag() very slow. Its important to switch from the file backend to some other backend if upgrading from TYPO3 4.3, especially if the file backend was used for content caches.

Note: Under heavy load the maximum set() performance depends on the maximum write and seek performance of the hard disk. If, for example, the server system shows lots of I/O wait in top, the file backend has reached this bound. A different storage strategy like RAM disks, battery backed up RAID systems or SSD hard disks might help then.

Options

Option Description Mandatory Type Default
cacheDirectory The directory where the cache files are stored. By default it is assumed that the directory is below TYPO3_DOCUMENT_ROOT. However, an absolute path can be selected, too. Every cache should be assigned an own directory, otherwise flush of one cache would flush all other caches with the same directory as well. No string typo3temp/cache/

Wincache backend

Note: Needs documentation, but behaves very similar to apc

t3lib_cache_backend_PdoBackend

The PDO backend can be used as a native PDO interface to databases which are connected to PHP via PDO. It can be used as an alternative to the database backend if a cache should be stored in a database which is otherwise only supported by TYPO3 dbal to reduce the parser overhead.

The garbage collection is implemented for this backend and should be called to clean up hard disk space or memory.

Note: There is currently very little production experience with this backend, especially not with a capable database like Oracle. In FLOW3 the PDO backend is the main database backend and we appreciate any feedback for real life use cases of this cache.

Options

Option Description Mandatory Type Default
dataSourceName Data source name for connecting to the database. Examples:
  • mysql:host=localhost;dbname=test
  • sqlite:/path/to/sqlite.db
  • sqlite::memory
Yes string
username Username to use for the database connection No string
password Password to use for the database connection No string

t3lib_cache_backend_TransientMemoryBackend

The transient memory backend stores data in a PHP array. It is only valid for one request. This becomes handy if code logic needs to do expensive calculations or must look up identical information from a database over and over again during its execution. In this case it is useful to store the data in an array once and just lookup the entry from the cache for consecutive calls to get rid of the otherwise additional overhead. Since caches are available system wide and shared between core and extensions they can profit from each other if they need the same information.

Since the data is stored directly in memory, this backend is the quickest backend available. The stored data adds to the memory consumed by the PHP process and can hit the memory_limit PHP setting.

t3lib_cache_backend_NullBackend

The null backend is a dummy backend which doesn't store any data and always returns false on get(). This backend becomes handy in development context to practically "switch off" a cache.

How to use the caching framework in extensions

This chapter is targeted at extension authors who want to use the caching framework caches for arbitrary needs. This section is a Howto about proper initialization but not a discussion about identifier, tags and lifetime decisions that must be taken during development.

Existing examples

These extensions use the caching framework for own caches and are at least in beta state:

  • extbase: Implements two different caches, takes care of proper initialization and usage.
  • tt_news: Implements one cache but the implementation has some bugs in version 3.0.
  • enetcache: Implements a generic content element caching solution which can be used by extensions, takes care of needed page cache manipulation, provides documentation with more details about the frontend cache system and tag handling best practice.

Cache registration and usage for extensions supporting only TYPO3 4.6 and above

If the extension supports only TYPO3 4.6 and above, cache registration and usage is very simple.

First, register a new cache in ext_localconf.php. The examble below just defines a sub array in cacheConfigurations. The is_array check is done to enable administrators to overwrite configuration of caches. In this cache neither frontend nor backend are defined, the cache manager will choose the variable frontend and the database backend by default in this case.

PHP script:
if (!is_array($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache'])) {
    $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache'] = array();
}

If special settings are needed, for example a specific backend (like the transient memory backend), it can be defined with an additional line below the cache array declaration. Extensions should not force specific settings, therefore the selection is again encapsulated in an if (!isset()) to allow administrators to overwrite those settings in localconf.php or other extensions loaded afterwards if needed. It is recommended to set up a cache configuration with sane defaults, but administrators should always be able to overwrite them. We assume they know what they are doing, for example it could happen that they want to use an own implementation of a specific backend that is delivered in an own extension:

PHP script:
if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['backend'])) {
    $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['backend'] = 't3lib_cache_backend_TransientMemoryBackend';
}

To get an instance of a cache, $GLOBALS['typo3CacheManager']->getCache('cacheName') should be used. The cache manager will return the fully initialized cache instance:

PHP script:
$myCacheInstance = $GLOBALS['typo3CacheManager']->getCache('myext_mycache');

Cache registration and usage for extensions supporting TYPO3 4.3 and above

If developing extensions that must support a wide range of TYPO3 core versions the usage of own caches based on the caching framework is more complicated.

A cache should be registered early in the TYPO3 bootstrap process, best place to do so is the ext_localconf.php file of the extension. Example from ext_localconf.php of extension 'myext' with a cache called 'mycache':

PHP script:
// Register cache 'myext_mycache'
if (!is_array($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache'])) {
    $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache'] = array();
}
// Define string frontend as default frontend, this must be set with TYPO3 4.5 and below
// and overrides the default variable frontend of 4.6
if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['frontend'])) {
    $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['frontend'] = 't3lib_cache_frontend_StringFrontend';
}
if (t3lib_utility_VersionNumber::convertVersionNumberToInteger(TYPO3_version) < '4006000') {
    // Define database backend as backend for 4.5 and below (default in 4.6)
    if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['backend'])) {
        $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['backend'] = 't3lib_cache_backend_DbBackend';
    }
    // Define data and tags table for 4.5 and below (obsolete in 4.6)
    if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options'])) {
        $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options'] = array();
    }
    if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options']['cacheTable'])) {
        $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options']['cacheTable'] = 'tx_myext_mycache';
    }
    if (!isset($TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options']['tagsTable'])) {
        $TYPO3_CONF_VARS['SYS']['caching']['cacheConfigurations']['myext_mycache']['options']['tagsTable'] = 'tx_myext_mycache_tags';
    }
}

If you are targeting 4.5 and above use t3lib_utility_VersionNumber::convertVersionNumberToInteger, for previous versions have a look at "Compatibility Code with Older TYPO3 Versions"

If the database backend is chosen, the extension must add required tables to ext_tables.sql. Those tables will only be used in TYPO3 4.5 and below:

SQL:
#
# Table structure for table 'tx_myext_mycache'
#
CREATE TABLE tx_myext_mycache (
    id int(11) unsigned NOT NULL auto_increment,
    identifier varchar(250) DEFAULT '' NOT NULL,
    crdate int(11) unsigned DEFAULT '0' NOT NULL,
    content mediumblob,
    lifetime int(11) unsigned DEFAULT '0' NOT NULL,
    PRIMARY KEY (id),
    KEY cache_id (identifier)
) ENGINE=InnoDB;

#
# Table structure for table 'tx_myext_mycache_tags'
#
CREATE TABLE tx_myext_mycache_tags (
    id int(11) unsigned NOT NULL auto_increment,
    identifier varchar(250) DEFAULT '' NOT NULL,
    tag varchar(250) DEFAULT '' NOT NULL,
    PRIMARY KEY (id),
    KEY cache_id (identifier),
    KEY cache_tag (tag)
) ENGINE=InnoDB;

An extension should usually not depend on $TYPO3_CONF_VARS['SYS']['useCachingFramework'] = true. This variable is always true in TYPO3 4.6, but could be false in versions below. It should be possible to use the caching framework extension cache even if the core caching framework caches are not used. To achieve this, an extension must not expect the cacheManager and cacheFactory classes to be already instantiated and available in global scope when the cache is accessed.

A typical initialization routine could look like this:

PHP script:
class tx_myext_myFunctionality {
    /**
     * @var t3lib_cache_frontend_AbstractFrontend
     */
    protected $cacheInstance;

    /**
     * Constructor
     */
    public function __construct() {
        $this->initializeCache();
    }

    /**
     * Initialize cache instance to be ready to use
     *
     * @return void
     */
    protected function initializeCache() {
        t3lib_cache::initializeCachingFramework();
        try {
            $this->cacheInstance = $GLOBALS['typo3CacheManager']->getCache('myext_mycache');
        } catch (t3lib_cache_exception_NoSuchCache $e) {
            $this->cacheInstance = $GLOBALS['typo3CacheFactory']->create(
                'myext_mycache',
                $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['myext_mycache']['frontend'],
                $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['myext_mycache']['backend'],
                $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['myext_mycache']['options']
            );
        }
    }
}

Calling t3lib_cache::initializeCachingFramework() ensures that the cacheManager and cacheFactory instances are available in TYPO3 4.5 and below. After calling initializeCache(), all available frontend operations like get(), set() and flushByTag() can be executed on $this->cacheInstance.

Typical cache access logic

Cache usage patterns are usually wrappers around the main code sections. Typical pseudo code looks like this:

PHP script:
    protected function getCachedMagic() {
        $cacheIdentifier = $this->calculateCacheIdentifier();

        // If $entry is null, it hasn't been cached. Calculate the value and store it in the cache:
        if (false === ($entry = $GLOBALS['typo3CacheManager']->getCache('myCache')->get($cacheIdentifier))) {
            $entry = $this->calculateMagic();

            // [calculate lifetime and assigned tags]

            // Save value in cache
            $GLOBALS['typo3CacheManager']->getCache('myCache')->set($cacheIdentifier, $entry, $tags, $lifetime);
        }
        return $entry;
    }

Using has() and get()

It isn't needed to call has() before accessing cache entries with get() as get() returns NULL if no entry exists.