Cache Basic API Guide

From Joomla! Documentation

Introduction[edit]

This is one of a series of API guides, which aim to help you understand how to use the Joomla APIs by detailed explanations and sample code that you can easily install and run.

This API guide explains in basic terms how to use the Joomla Cache API. The Cache API can be used as a simple dictionary or based on a Callback type of CacheController and File type of CacheStorage. Other types are mentioned, but not described in detail.

At the end of the guide, you can find the code for a simple Joomla module that you can use and adapt to experiment with the Cache APIs.

As a general rule, you should use cache on the Joomla site only, not on the administrator Backend, and the descriptions below relate to when you have the Global Configuration (System tab) parameter System Cache set to On – Conservative caching and the System – Page Cache plugin is disabled.

Sample Usage[edit]

// Try to load the results from cache.
$cache   = JFactory::getCache('com_sample', '');
$cacheId = 'com_sample-' . $id . $otherIds;

// Check the cached results.
if ($cache->contains($cacheId))
{
  return $cache->get($cacheId);
}
...
// Store the data in cache.
$cache->store($data, $cacheId);

Callback Cache API - Overall Description[edit]

Joomla Cache Overview

The diagram provides an overview of how Joomla cache works and the description below relates to the circled steps in the diagram. The code in green is your code: the code in yellow is Joomla code.

Let's imagine that in the main code of our extension we have to get some data and we separate out this code into a function getData(). Typically this function will involve a database query that may take a significant amount of time.

Step 1 relates to the case where we don't use cache – we simply call getData() and store the results returned.

Steps 2 to 5 relate to the case where we use cache.

Step 2: we call $cache = Factory::getCache() with the following parameters:

  • parameter 1 ($group) should be set to the name of our extension. This is so that an administrator can see that extensions are storing cache, and can clear cache for specific extensions. (Some core Joomla code passes _system as parameter 1). For cache stored in the file system, this also relates to the subdirectory of the cache folder in which the cache files are stored.
  • parameter 2 ($handler) is the type of cache controller, which in this guide is callback. Other types that Joomla supports are output, page and view.
  • parameter 3 ($storage) should be left null so that Joomla picks up the cache storage mechanism from Global Configuration.

What is returned into $cache is a pointer to an instance of a CacheController subclass, based on what we pass as parameter 2. When we pass callback we get returned an instance of CallbackController.

Step 3: we call $data = $cache->get() with 3 parameters:

  • parameter 1 ($callback) is the name of the callback function, that in our case is getData. You can also pass an anonymous function by e.g.
$myFunc = function() { … };
$cache->get($myFunc, …);
  • parameter 2 ($args) is an array of the arguments to pass to the callback function.
  • parameter 3 ($id) is an id to be associated with our cache. For file storage this will factor into the name of the file in which the cached data is stored. If your extension caches data in several places you must use different ids for each.

Important If the data you're caching involves a database query whose results differ for different users take that into account when generating this cache id. Otherwise, users may see cached results that don't reflect what their privileges allow, causing possible confusion or security breaches.

If the query takes into account the Access field of a database record, then you can lump together the cache for users with the same Access rights by using:

$groups = implode(',', Factory::getUser()->getAuthorisedViewLevels());
$cacheId = $groups . "some text string";

Similarly, if you have a multilingual site then you want to separate the cache for different languages. However, Joomla does incorporate the current language tag (Factory::getLanguage()->getTag()) when it generates the cache file name, so you don't have to worry about this yourself.

Step 4 The Joomla code checks that caching is enabled and, if so, attempts to find the file that contains the cache. If it finds it, it reads the data from the file and returns the (deserialized) data to the calling function.

Step 5 Still within the case of caching is enabled, if the cached file can't be found, the Joomla code calls the callback function and captures the returned data. It then creates a cache file and stores the (serialized) data in it and returns the data to the calling function.

Step 6 If caching is not enabled, the code simply calls the callback function, captures the results and returns them to the calling function. It doesn't attempt to store the data in a cache file.

Sample Usage[edit]

/** @var JCacheControllerCallback $cache */
$cache = JFactory::getCache('com_modules', 'callback');

// To load the data when it is needed
$loader = function () use ($param1, $param2...) {
   ...
}

return $clean = $cache->get($loader, array(), $cacheId);

Joomla Cache Classes and Methods[edit]

Joomla Cache Classes

The Joomla cache classes are shown in the diagram. There are three fundamental classes (shown along the bottom):

  1. CacheController – this acts as a parent class for the specific type of cache controller class that interacts with your code, as determined by your second parameter ($handler) in your Factory::getCache() call.
  2. Cache – a central Joomla cache class
  3. CacheStorage – this acts as a parent class for the specific type of cache storage class that interacts with your operating system's caching mechanism. The cache storage type used is determined by the setting of the System Cache Handler parameter within the Joomla Global Configuration.

In this API guide we are focusing on Callback Cache and File storage, which means that we will end up with CallbackController and FileStorage class objects. Our code interacts with the methods of the CallbackController object, and the FileStorage object handles the interaction with the cache files in the system filestore.

Important If you look at the Joomla 3.x APIs for the classes that inherit from CacheController (e.g. https://api.joomla.org/cms-3/classes/Joomla.CMS.Cache.Controller.CallbackController.html) you will see that the methods get and store are marked as deprecated. However, this isn't actually the case. It is the get and store methods within the CacheController class that are deprecated, the intention being that they will be removed from the parent CacheController in Joomla v4 and these methods would be present only in the subclasses. However the PHP documenter sees the deprecated mark against the method in the parent class and automatically applies it (incorrectly) to the associated method in the subclass.

Also if you look at the PHP source code for the CallbackController class (currently in libraries/src/Cache/Controller/CallbackController.php), you will see that the signature of the get method in that class is different from that of the get method in the parent CacheController class, and again the PHP documenter outputs the method API incorrectly, based on the parent class rather than on the subclass.

These problems should disappear with the Joomla v4 APIs, when the get and store methods are removed from the parent CacheController class.

File Cache Storage[edit]

The use of File as the cache storage mechanism means that Joomla will write files in the folder defined by the Global Configuration parameter (System tab) Path to Cache Folder. If this is blank the default folder is used (as defined by JPATH_CACHE in includes/defines.php), which is /cache under the document root for Joomla 3.x and /administrator/cache under the document root for Joomla v4.

Assuming you are using Conservative Caching, each component, module or plugin has its own folder under the generic cache folder. (The folder name is based on the $group parameter that you pass to Factory::getCache()).

Within each folder are the cached files that you can edit and view the cached serialized data. The filename of each file is based (partly) on the $id parameter in your $cache->get() call described above.

Cache APIs[edit]

Most of the methods have already been described, but here is a summary of the key methods that you may wish to call from your code.

Accessing Cache[edit]

To get a handle of the cache you should first call:

use Joomla\CMS\Factory;
$cache = Factory::getCache('com_example', 'callback');

where you would replace com_example with the name of your component, module or plugin. This function returns to you the instance of the cache CallbackController class.

get method[edit]

Once you have a handle on the cache CallbackController instance you can call something like:

$results = $cache->get($callback, $args, $id);
// for example ...
$results = $cache->get('getData', array('p1', 'p2'), 'getData query');

where

  • $callback is a string with the name of the callback function, or a variable that has been assigned to an anonymous function (as described above).
  • $args is an array of the arguments you want to pass to your callback function
  • $id is a string that is unique enough to identify this cache (taking into account different users as described above).

Within the code for the get method Joomla does raise exceptions, but these relate to the case where you have tried to use a cache mechanism that is not supported. Even so, you may still wish to include your call to get within a try/catch block.

getCaching method[edit]

You can check if caching is enabled by calling

$enabled = $cache->getCaching();

This will return true if either Conservative Caching or Progressive Caching is enabled.

store method[edit]

You can store data into a cache file by calling

$result= $cache->store($data, $id, $group);

where

  • $data is the data you want to store
  • $id is the id you want to associate with that data cache
  • $group is your extension name (e.g. com_example).
  • $result returned is true or false, depending upon whether the data was stored successfully or not.

However, you will usually not need to call this function directly, because if you use the cache get method then this will store it for you automatically.

Sample Module Code[edit]

Module Installation[edit]

Below is the code for a simple Joomla module that you can install and run to demonstrate the use of the Joomla cache functionality. If you are unsure about development and installing a Joomla module, following the tutorial at Creating a simple module will help.

In a folder mod_demo_cache create these two files:

mod_demo_cache.xml

<?xml version="1.0" encoding="utf-8"?>
<extension type="module" version="3.1" client="site" method="upgrade">
    <name>Cache demo</name>
    <version>1.0.1</version>
    <description>Code demonstrating use of Joomla Cache</description>
    <files>
        <filename module="mod_demo_cache">mod_demo_cache.php</filename>
    </files>
</extension>

mod_demo_cache.php

<?php
defined('_JEXEC') or die('Restricted Access');

use Joomla\CMS\Factory;

echo "<br>Current time is " . date("h:i:sa") . "<br>";

$cache = Factory::getCache('mod_demo_cache', 'callback');

$caching = $cache->getCaching();
if ($caching)
{
	echo "<br>Caching enabled<br>";
}
else
{
	echo "<br>Caching not enabled<br>";
}

$myquery = function ()
{	// This function calculates the biggest 3 tables from the selection below
	$tables = array("#__assets", "#__overrider", "#__content", "#__extensions", "#__menu", "#__updates", "#__ucm_history", "#__finder_terms_common");
	$db = Factory::getDbo();
	foreach ($tables as $name)
	{
		$query = $db->getQuery(true)
			->select('count(*)')
			->from($name);
		$db->setQuery($query);
		$totals["$name"] = $db->loadResult();
	}
	arsort($totals);
	return $totals;
};

$start = microtime(true);
try
{
	$results = $cache->get($myquery, array(), "top tables", false);
	$delay = microtime(true) - $start;
	echo "<br>$delay ms delay<br>";
}
catch (\JCacheException $cacheException)
{
	$results = $myquery();
	$delay = microtime(true) - $start;
	echo "<br>$delay ms delay (in cache exception)<br>";
}
echo "<h3>Top 3 tables</h3>";
$i = 0;
foreach ($results as $key => $value)
{
	echo "$key: $value<br>";
	$i++;
	if ($i == 3) break;
}

Zip up the mod_demo_cache directory to create mod_demo_cache.zip.

In your Joomla Administrator, go to Install Extensions and via the Upload Package File tab, select this zip file to install this sample cache module.

Make this module visible by editing it (click on it within the Modules page):

  1. making its status Published
  2. selecting a position on the page for it to be shown
  3. on the menu assignment tab, specify the pages it should appear on

When you visit a site web page you should see the module in your selected position.

Module Explanation[edit]

The anonymous function assigned to $myquery performs SQL COUNT operations on a set of tables, and then reorders the resulting $totals array in size order, outputting the most populous three tables.

If caching is enabled, the results of running $myquery are stored in a cache file, and if you repeatedly reload the web page you will see the time difference in running the full query versus obtaining the results from cache.

The module also displays the current time. If when you reload the page the current time doesn't change, it means that the whole module output is being cached, and not being run on page reload. This will happen if:

  • there is no user logged in, and
  • either Progressive Cache is on or the System Page Cache plugin is enabled.

If Progressive Cache is on the output of all the modules are stored in cache (in the cache/com_modules directory), and upon page reload, the cache is used, rather than running the code of the individual modules.

If Page Cache is enabled, the whole web page is cached (in the cache/page directory).

It's instructive to experiment with the various cache settings, view the cache files in the file system and use the Administrator Clear Cache functionality to clear the cache for this specific module.

Related Material[edit]

This Cache page describes the use of Joomla cache on a site.

The Joomla MVC Component Development tutorial has a page on Adding Cache into your component.

See also Using caching to speed up your code.