Cache Basic API Guide

From Joomla! Documentation

Introduction

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

This API guide explains in basic terms how to use the Joomla Cache API, and focuses on the 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 which 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 back-end, 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.

Overall Description

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 which 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 which 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 is this guide is "callback". Other types which Joomla supports are "output", "page" and "view".
  • parameter 3 ($storage) should really 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, which in our case is "getData". You can also pass an anonymous function by eg
$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, so 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 then you should take that into account when generating this cache id. Otherwise users may see cached results which 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 will 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 it attempts to find the file which contains the cache. If it finds it then it reads the data from the file and returns the (deserialized) data to the calling function.

Step 5 Still within the case of caching being enabled, if the cached file can't be found then 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 then 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.

Joomla Cache Classes and Methods

Joomla Cache Classes

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

  1. CacheController – this acts as a parent class for the specific type of cache controller class which 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 which 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 focussing 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 which inherit from CacheController (eg 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 which 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

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 then 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 then each component / module / plugin has its own folder under the generic cache folder. (The folder name is based on the $group parameter which you pass to Factory::getCache()).

Within each folder are the cached files which 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

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

Accessing Cache

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 / plugin. This function returns to you the instance of the cache CallbackController class.

get method

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 which 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 which 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 which is not supported. Even so, you may still wish to include your call to get within a try/catch block.

getCaching method

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

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 (eg "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

Module Installation

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

In a folder mod_demo_cache create the following 2 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.

Within 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) then:

  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 then you should see the module in your selected position.

Module Explanation

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 3 tables.

If caching is enabled then the results of running $myquery are stored in a cache file, and if you repeatedly reload the web page then 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, then 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 then the output of all the modules are stored in cache (in the cache/com_modules directory), and on page reload the cache is used, rather than running the code of the individual modules.

If Page Cache is enabled, then 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

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.