Website performance has been the key factor for user engagement. Since more and more users are using tablets and mobiles for internet access, it is very important to have a fast loading website in place. Though there are multiple ways to enhance your site, I would like to concentrate on caching. I have implemented caching using the following:
CodeIgniter: I have been working with CodeIgniter for almost 8+ years. Since then, the framework has been my favorite. We will be using CodeIgniter3 for this tutorial. Though it should be easy to work with any framework.
PhpFastCache is a good caching library that supports multiple database drivers. You can read more here. I could have used the default cache feature in CodeIgniter. But PhpFastCache provides better handling when it comes to cache management. Installing the library is pretty straight forward. Hoping you are familiar with composer, use the following command
composer require phpfastcache/phpfastcache
Redis: Redis is a in-memory database. It is really fast for saving and accessing data because all of the data is saved in memory & not the disk. Installing it over Ubuntu us pretty simple. Use the following command
sudo apt-get install redis-server
Once installed, you can manage your Redis server with following commands:
sudo service redis-server start/stop/restart
To work on Redis with PHP you need to install the php-redis extension. You can do this as follows:
sudo apt-get install php-redis
sudo service apache2 restart // restart the apache server
Once you are done with all this, you are ready to dive in the code now.
Library: Cachelip.php
First you need to create a library class file for the cache library. The library would have a few functions and a constructor. Let us name this file Cachelib.php. The file will have the following code
<?php if (!defined('BASEPATH'))
{
exit('No direct script access allowed');
}
/* the use statements from PhpFastCache */
use Phpfastcache\CacheManager;
use Phpfastcache\Drivers\Redis\Config;
Some private variables and a constructor to load & connect to the Redis instance
/**
* Cache library
*/
class Cachelib
{
private $CI;
private static $instance = null;
private $conn;
public function __construct()
{
$this->CI = &get_instance();
try {
$this->conn = CacheManager::getInstance(
'redis',
new Config(
array(
"host" => "127.0.0.1",
"port" => 6379,
"database" => 0 // redis databases are identified by index
)
)
);
}
catch (Exception $e)
{
$message = $e->getMessage();
$messageArray = explode("line", $message);
// Log the error message if required
show_error($message, 500);
}
}
A function to get the instance for the connection manager
/**
* function to get connection instance
*
* @return object
*/
public function getConnection()
{
return $this->conn;
}
Next function is important to understand. Redis provides key-value pair to save the data. The PhpFastCache library extends this functionality by providing extra features like adding tags to the the key-value pair. A key-value pair is good to have. But the problem starts when you have to delete the bulk data. Let us take an example of an author profile. Let us say that the authors have a unique profile_id but they also have some attributes like language. You can delete the the cache value with the key = profile_id. The problem usually starts when you have to delete the cache for all authors by language. Since Reid only allows key-value pairs, the PhpFastCache allows you to put tags to the values as well.
ID Name Book Language 01 Harry Book1 English 02 Zarah Book2 Arabic 03 Michael Book5 English 04 Terry Book3 Punjabi 05 Tony Book6 Arabic
We can delete the above cache with ID as a key, but with this library it is also possible to delete all cache values with language english only. All we need to do is set tag(s) while saving the data. The function to save such data would look like this.
/**
* function to set cache
*
* @param string $key the key for the value
* @param mixed $data the data needs to be saved
* @param array $tag the tag for data
* @param integer $expire in seconds
* @return void
*/
public function setCache($key, $data, $tag=array(), $expire = 0)
{
if (!$texpire) {
$expire = $this->CI->config->item("cacheExpireTime"); // from the config file
}
if (!empty($tag) && is_string($tag)) {
$tag = array($tag);
}
$CachedString = $this->conn->getItem($key);
$CachedString->set($data)->expiresAfter($expire)->setTags($tag);
$this->conn->save($CachedString);
}
This piece of code would save your data with a key and multiple tags associated to it. For an example, I can save some information as follows:
//Save Harry's record with tags "harry" & "english"
$this->cachelib->setCache(
"01",
"Harry wrote book1 ",
array(
"harry",
"english"
)
);
//Save Michael's record with tags "michael" & "english"
$this->cachelib->setCache(
"01",
"Harry wrote book5 ",
array(
"michael",
"english"
)
);
With the code above, Redis will save two records with ID and other records with TAGS pointing to the original records. The PhpFastCache library handles Redis pretty well with with. With this setup user can access & delete data with tags as well. Below are the other required functions.
/**
* function to get cache data
*
* @param string $key
* @return mixed
*/
public function get($key)
{
return $this->conn->getItem($key)->get();
}
/**
* function to clear cache for
* @param string $for
* @param string $by
* @return boolean
*/
public function clear($for = "all", $by="tag")
{
if ($for == "all")
{
return $this->conn->clear();
}
else
{
if ($by == "tag") {
return $this->conn->deleteItemsByTag($for);
}
elseif($by == "key"){
return $this->conn->deleteItem($for);
}
else{
return $this->conn->deleteItem($for);
}
}
}
/**
* function to check if key is already cached
*
* @param string $key
* @return boolean
*/
public function isCached($key)
{
$CachedString = $this->conn->getItem($key);
return $CachedString->isHit();
}
Controller: Welcome.php
Let us move on to the controller and how should we be using our library functions to manage the data with Redis. Since now you have the weapon, it depends on you to use it. Let us dive into more details to get this done.
The welcome controller should look like this:
<?php defined('BASEPATH') or exit('No direct script access allowed');
class Welcome extends CI_Controller
{
/**
* index function
* @author Harpreet Bhatia
* @return html
*/
public function index()
{
/* key name */
$key = "home_page_key"
/* get ready with tags */
$tags = array(
$key,
"some_tag",
"module_name"
);
/* check if key has been cached */
if ($this->cachelib->isCached($key))
{
/* get the cache value */
$html = $this->cachelib->get($key);
$this->load->view(
"html", array(
"html" => $html
)
);
}
else /* if the key was not previously cached */
{
/* detch all the data here */
/* $data["content"] = $this->model->some_function() */
$data["template"] = "home/index";
$html = $this->load->view("template", $data, true);
/* save data to cache */
$this->cachelib->setCache($key, $html);
/* WITH TAGS =====> $this->cachelib->setCache($key, $html, $tags); */
/* load the view */
$this->load->view(
"html", array(
"html" => $html
)
);
}
}
}
As you can see above the code checks for existing cache. If found, gets the value(the processed HTML) and load it. If not found, we fetch all required data and pass it to HTML. If you see it clearly we are caching the whole html instead of the $data
. Caching & displaying HTML is better and faster than caching just the $data
.
You can use the library functions to delete the cache with key or tags.
You can delete home page cache with the key.
$this->cachelib->clear("home_page_key", "key");
Or you can delete multiple cache with tags:
$this->cachelib->clear("module_name", "tag");
I hope it helps you guys. I am open for opinions & suggestions.