Quick links: Content - sections - sub sections
EN FR

Caching data or parts of an HTML page allows to increase performance of your web site, and even to avoid to send data to the browser in some case.

You probably saw how to put zone content in cache. You will see here how to put any data in a cache system, and how to use HTTP cache.

jCache

jCache is a class to store any data in a cache system like memcache, Redis... It supports other kind of storage like files or a SQL database.

Configuration

You should declare storage in a var/config/profiles.ini.php file. See the corresponding chapter to know how to use this file. It may contain some section, corresponding to a "profile", like for jDb or jKVDb.

The connection type to indicate in profile names is jcache.

Each profile must declare these properties:

  • driver: the driver name for the storage backend
  • enabled: to enable or disable the cache for this profile (1 or 0). You can then disable the cache without modifying your classes, when you develop or want to debug something.
  • ttl: the default time to live of data, in seconds. 0 means no timeout.
  • automatic_cleaning_factor : indice for the automatic cleanup of the cache backend

Depending of the driver, you may have additional parameters.

Here is an example in the profiles.ini.php file:


[jcache]
default = foo

[jcache:foo]
enabled = 1
driver = file
;  other parameters....

memcache backend

It is a driver for memcache, using the memcache API of PHP (not memcached API).

The configuration parameter is server, containing a list of host and port.


[jcache:mymemcache]
driver=memcache
ttl=360
enabled=1
servers = memcache_host1:11211,memcache_host2:11211,memcache_host3:11211

Redis backend

This driver stores data in a Redis database.

In fact, there are two plugins:

  • "redis_ext": it uses the API of the PHP extension Redis (plugin introduced in Jelix 1.6.14)
  • "redis_php": it uses a pure PHP class, PHPRedis to communicate with Redis. (plugin introduced into Jelix 1.6.8 under the name "redis" and renamed to "redis_php" in Jelix 1.6.14). Since Jelix 1.7, it is available in a separate module, in the Composer package "jelix/php-redis-plugin".

Possible parameters:

  • host : name or ip of the Redis server
  • port : network port
  • db : the database number in Redis (0 by default)
  • key_prefix : a prefix which will be added on each key
  • key_prefix_flush_method: the method to delete keys when a prefix is indicated.

Example:


[jcache:myredis]
driver=redis_ext
ttl=360
enabled=1
host=192.168.0.1
port=6379
db=0
key_prefix=
key_prefix_flush_method=

If you indicate a prefix, all keys you indicate to jCache will be prefixed by the key_prefix value into the Redis database.

Moreover, when the cache is asked to be flushed, only keys having the prefix will be deleted. However, because of how Redis is working, it may be very slow, and then "freeze" your application.

So the plugin implements different methods to do the flush in this case. You indicate the method into the key_prefix_flush_method parameter:

  • direct : keys are deleted directly. This is the default behavior, but should not use it if you know that your database could have hundred keys.
  • jcacheredisworker : the deletion will be made asynchronously by a worker or a cron script. See details below.
  • event : if you want an other behavior, you should implement it (and it is better if it is an asynchronous process). Then you can notify your implementation by listening the jCacheRedisFlushKeyPrefix event jEvent. This event contains the profile name and the prefix of keys to delete.

How is the jcacheredisworker method working?

When a flush is asked, the plugin only stores the prefix into a Redis list, named jcacheredisdelkeys. A worker (a process that run aside the web server) or a cron script (a script that is launched by the server periodically), should then remove these prefixes from the list and delete corresponding keys.

You have an example of a such worker in lib/jelix/core-modules/jelix/controller/redisworker.cmdline.php. You can launch it with systemd/sysinit/supervisord for example. The command is: php myapp/console.php jcache:redis:delete <profile>. Replace <profile> by the profile name of the cache. It waits after incoming prefixes in the list, and delete corresponding keys. If you launch it by hand on the command line, just hit <ctrl>+C to stop it.

file backend

It is similar to the file driver of jKVDb. Here are its configuration parameters:

  • driver=file
  • cache_dir: the directory where to store the files. by default: temp/yourapp/cache/.
  • file_locking=1 to disable or enable file locking (keep to 1 is better)
  • directory level: Set the directory structure level. 0 means "no directory structure", 1 means "one level of directory", 2 means "two levels"...
  • directory_umask: umask for directory structure (default '0700')
  • file_name_prefix: prefix for cache files (default 'jelix_cache'). Since Jelix 1.6.8, it is used as prefix for the root directory of the cache.
  • cache_file_umask: umask for cache files (default '0600')

sql backend

This driver uses a sql table to store cache values. The installer of jelix creates the needed table if you use this backend. If it is already installed, during developpement, use one of the lib/jelix/core-modules/jelix/install/sql/install_jcache.schema.* files to create the table.

Configuration parameters:

  • driver=db: value is "db".
  • dao: indicates the dao to use. By default, jelix~cache.
  • dbprofile: the jDb profile to use.

Using jCache

To store and read from the cache system, call one of the static methods of jCache.

  • jCache::get($key): to retrieve the value corresponding to the given key. False means that the key does not exist.
  • jCache::set($key, $value, $ttl): to store a new value or modify an existing value. $ttl is optional
  • jCache::delete($key): delete the given key-value
  • jCache::increment($key): increment the value. You can indicate also the value of the incrementation
  • jCache::decrement($key): decrement the value. You can indicate also the value of the decrementation
  • jCache::add($key, $value): to store a new value. return false if the key already exists
  • jCache::replace($key,$value, $ttl): to change the value of the corresponding key. If the key doesn't exist, it returns false. $ttl is optional.
  • jCache::garbage(): delete all expired keys
  • jCache::flush(): delete all keys
  • jCache::call($fn, $fnargs, $ttl): get the value corresponding to the function name/args. If it is not set, call the function $fn with arguments $fnargs, which should return a value. This value is then put into the cache.

All this static methods accept as last argument a profile. By default, the "default" profile will be used.

Only these charaacters should be used for key names : letters, numbers, /, _, -, :, ..

jResponse and HTTP cache

The HTTP protocol defines some headers for the browser to say how to store the resource (the web page), if the browser can use its local cache system or not. So the browser can know if it should do a request, or, if it have to do a request, it can indicate to the server informations about the cache status. In the server side, we can then know if we have to regenerate the resource, or if we can say that the browser must use the content in its cache. It then saves bytes and bandwidth.

The class jResponse has methods to manage easily the HTTP cache. These methods are available for all responses, so you can activate HTTP cache for HTML page, images, JSON content etc. Don't forget that the cache is client side, not server side.

There are two ways to manage the browser cache: cache by expiration and cache by validation.

Warning: in most of cases, except if you use the method setLifeTime with a private cache, the HTTP cache should be used only for content that is not personnalized. The content should not include private data for example. It may be stored by some proxies, and then served to all users, because proxies don't use cookies (session cookies) as discriminant characteristics to store cache.

Cache by expiration

This type of cache is a cache that will be kept valid until a specific date. Before this date, the browser will not ask the page to the server and will display the version stored in its cache. However, after this date, the browser will request the page to the server. You can specify the expiration date, by indicating the date, (method setExpires), or by indicating an mount of time (method setLifeTime).

setExpires

The method setExpires allows to specify the date of the end of the validity of the cache. It adds an "Expires" HTTP header.

In the controller:4


$rep->setExpires($date);

// or

$rep->setExpires(“+1 days”);

The parameter expected by setExpires is a date, representing by a jDateTime or DateTime object, or a string compatible with strtotime.

setLifeTime

The method setLifeTime allows to specify the life time (in seconds) of the cache. You have the possibility to specify if the resource can be cached by any cache system on the network (proxies for example) or if this resource is private and can be cached only by the browser of the user. By default, it is private.

This method adds a "Cache-Control" HTTP header.

In the controller:


$rep->setLifeTime(3600); //1 hour in private cache

//or 

$rep->setLifeTime(600, true);// 10 minutes in shared cache

Cache par validation

The cache by validation allows to use a more complex cache policy. The browser always asks the page to the server, but the server can decide to return the whole content of the resource, or to say to the browser that it can use its cache content.

So you have to decide in your controller, if you should return or not the content of the resource. To help you, there is a method, isValidCache, that will return you true if the cache stored in the browser is still valid. Give to this method, the date of the last modification of the resource ("Last-modified" HTTP header), or a token corresponding to the state of the resource ("Etag" HTTP header). So you have to retrieve this date or to calculate this token (a token can be any string, a md5 hash for example).

In the controller:


$dateLastModified = $foo->updated_at; // date of the last modification

if ($rep->isValidCache($dateLastModified)) {
   // the cache is ok, we stop here.
   return $rep;
}

// example with "Etag"
$etag = ... ; // generate the etag, corresponding to the state of the resource

if ($rep->isValidCache(null, $etag)) {
    // the cache has the same etag, we stop here
   return $rep;
}

// cache is not valid.
// generate the content here

The parameter expected by isValidCache is a date, representing by a jDateTime or DateTime object, or a string compatible with strtotime.