Page Cache - Benefits and How to

Written by in Tutorials on May 2, 2013  |  22 Comments

page-cache-benefits-and-how-to

What is page caching? How can I improve my website’s loading speed? That would usually be the question that brought you here. In this quick tutorial, we will explain what is page caching and how we can achieve it using PHP. However, for this tutorial, we will not be providing a download at the end, unlike other tutorials we had written.

What is page caching?

If you render your web pages dynamically for relatively static pages such as your information pages on OpenCart or maybe even your homepage, you might wish to consider this. Page caching is done to help reduce the numerous request to your database. The fetched contents are captured and stored during the first load, and in the future loads, they are served instead.

In simpler explanation, imagine you are asked to find out about the history of the United States and share it with your friend. You did a search on the internet, tries to remember it, and go back to him with the details. Two hours later, another friend asked you the same question, you will be repeating the same process. Imagine hundreds of friends asking you the same question, you will be repeating it over and over. If you, instead, had written down the details, you won’t be repeating the process. So that’s what caching does. It stores the data fetched and just renders to the next guy who asked for the same thing.

What are the benefits?

Nowadays, most websites use cache to help reduce page load time. For sure, there are tons of benefits to page caching.

Caching your data can help to reduce page loading speed. With a faster website, it does somehow benefit your SEO rank. Who knows, your visitors might be happier too!

How to cache my page?

Alright, this should be the part all of you would be interested in. We will attempt to cache your web page on OpenCart. If we look into OpenCart’s structure, we will be able to understand that each normal page on OpenCart are rendered using

$this->response->setOutput($this->render());

Further looking into it, you will find the response file in system/library/response.php and that all pages are routed through the index.php file.

Now here’s what we will do. We will capture the HTML in the response.php file and store to serve to future requests.

We will need a simple script to do caching. We will create a script in system/library/pagecache.php and also an empty folder (system/cache/pagecache)

We will define the basics at the top of the script.

<?php
define('CACHE_FOLDER', DIR_CACHE . 'pagecache/'); // Cache Folder
define('CACHE_EXPIRE', '2400'); // Cache Expire in seconds
define('SITE_LANGUAGE', 'en'); // Default website language
define('SITE_CURRENCY', 'USD'); // Default website currency
$skip_routes = array( // Skip routes to cache. We will not cache product/compare since it should be unique to all clients
'product/compare'
);

Now, we will check if session had been started.

if (!session_id()) {
ini_set('session.use_cookies', 'On');
ini_set('session.use_trans_sid', 'Off');
session_set_cookie_params(0, '/');
session_start();
}

Once the basics are ready, we will check for some conditions to ensure the user is not logged in or has items in their shopping cart.

if(!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'GET' && (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off')) {
if ((isset($_GET['route']) && !in_array($_GET['route'], $skip_routes)) || !isset($_GET['route'])) {
if(empty($_SESSION['customer_id']) && empty($_SESSION['affiliate_id']) && empty($_GET['affiliate']) && empty($_SESSION['cart'])) {

Once we checked, we will start to build up the file path where the cache would be stored.

$domain = $_SERVER['HTTP_HOST'];
$language = (empty($_SESSION['language']) || !is_string($_SESSION['language'])) ? SITE_LANGUAGE : $_SESSION['language'];
$currency = (empty($_SESSION['currency']) || !is_string($_SESSION['currency'])) ? SITE_CURRENCY : $_SESSION['currency'];
$url = http_build_query($_GET);
$cacheFile = CACHE_FOLDER . $domain . '_' . $language . '_' . $currency . '_' . md5($url) . '.html';
define('CACHE_FILE', $cacheFile);
$cacheTime = CACHE_EXPIRE;

Finally, we will check if the file exists, and output the file instead, followed by ending the whole script if the file exists.

if (file_exists($cacheFile) && time() - $cacheTime < filemtime($cacheFile)) {
	$file = gzopen($cacheFile, 'r');
	$file = gzread($file, 1999999);
	echo $file;
	exit;
}
}
}
}
?>

That will be all for a simple script. Next, we will have to edit system/library/response.php to capture the file. We will do a quick search for the function that was always called for in all controller files.

public function setOutput($output) {

Inside the function, we will attempt to capture the data. We will first check if the cache file path has been defined.

if (defined('CACHE_FILE')) {

Then, we will start working on it. We will write the contents into the file.

$cacheFile = CACHE_FILE;
$fp = gzopen($cacheFile, 'w9');
gzwrite($fp, $output);
gzclose($fp);
}

We are almost done now. I mentioned that every page is routed through the index.php file. We just have to do a simple ‘include’ in the index.php file to include the cache script we created. Do a quick search in index.php for the following line:

require_once('config.php');

and below it, just include your cache script.

require_once('system/library/pagecache.php');

You have now successfully created a script to cache your web pages! Below is how your page cache script should look like in the end.

<?php
define('CACHE_FOLDER', DIR_CACHE . 'pagecache/');	// Cache Folder
define('CACHE_EXPIRE', '2400');						// Cache Expire in seconds
define('SITE_LANGUAGE', 'en');						// Default website language
define('SITE_CURRENCY', 'USD');						// Default website currency
$skip_routes = array(								// Skip routes to cache
	'product/compare'
);
if (!session_id()) {
	ini_set('session.use_cookies', 'On');
	ini_set('session.use_trans_sid', 'Off');
	session_set_cookie_params(0, '/');
	session_start();
}
if(!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'GET' && (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off')) {
	if ((isset($_GET['route']) && !in_array($_GET['route'], $skip_routes)) || !isset($_GET['route'])) {
		if(empty($_SESSION['customer_id']) && empty($_SESSION['affiliate_id']) && empty($_GET['affiliate']) && empty($_SESSION['cart'])) {
			$domain = $_SERVER['HTTP_HOST'];
			$language = (empty($_SESSION['language']) || !is_string($_SESSION['language'])) ? SITE_LANGUAGE : $_SESSION['language'];
			$currency = (empty($_SESSION['currency']) || !is_string($_SESSION['currency'])) ? SITE_CURRENCY : $_SESSION['currency'];
			$url = http_build_query($_GET);
			$cacheFile = CACHE_FOLDER . $domain . '_' . $language . '_' . $currency . '_' . md5($url) . '.html';
			define('CACHE_FILE', $cacheFile);
			$cacheTime = CACHE_EXPIRE;
			if (file_exists($cacheFile) && time() - $cacheTime < filemtime($cacheFile)) {
				$file = gzopen($cacheFile, 'r');
				$file = gzread($file, 1999999);
				echo $file;
				exit;
			}
		}
	}
}
?>

There are also many tutorials on the internet that teaches about caching your web page using PHP. A quick search and you will find lots of them on the first page of Google. Most of them are for general websites, but this is specific to helping you kick start on OpenCart. You can definitely apply the same concepts learnt here on other PHP driven websites.

Page caching will help you in reducing page loading times, and in turn improve your SEO ranking and display higher up in search engines like Google and Bing. You can always consider purchasing other SEO tools for your OpenCart store. There are tons of SEO extensions out there for OpenCart, but always remember to pick one that achieves what you have in mind. We do offer one SEO pack, and you can look into it on our web store too.

Tags: , , ,

About the Author

Joined OpenCart community since November 2011. MarketInSG is Singapore's first website dedicated to OpenCart web designing & modification.

View all posts by

22 Comments on "Page Cache - Benefits and How to"

  1. Edgar November 30, 2015 at 3:25 am · Reply

    Yes it works for 1.5.6, thanks a lot

  2. Mikell April 2, 2015 at 7:03 pm · Reply

    Hi, seems a good job but it’s something that to me does not work. Please help me..
    I did what you said but then can not get into the administration site (mysite.com/admin). When access mysite.com/admin page gives me a blank page. No errors can not see them because I dont access the management interface of the site.
    I’m not sure if I added well in admin/index.php line require_once(‘system/library/pagecache.php’);

    I added it so:
    // Configuration
    if (file_exists(‘config.php’)) {
    require_once(‘config.php’);
    require_once(‘system/library/pagecache.php’);
    }

    if I comment the line require_once(‘system/library/pagecache.php’); i.e.
    // require_once(‘system/library/pagecache.php’); I have no blank page instead of the admin panel (mysite/admin)
    Thx.
    (O.C 1.5.5.1)

  3. cyber_phoenix October 8, 2014 at 7:56 am · Reply

    Is there any way to delete pages cache without entering in ftp and deleting manually ?

  4. Cleo May 27, 2014 at 12:09 am · Reply

    Hello

    I did this and everything is working ok but what I’m wondering is right now I have only 300 product and since I added this page cache about 1 month ago I have about 30,000 pages in the pagecache folder, is this normal? Should I delete them sometime?

    Regards
    Cleo

    • Cleo May 29, 2014 at 7:23 am ·

      Hello again

      I went through the pagecache folder and found out that I have multiple copy of the same file! I compare them in notepad++ and they are exactly the same :(

      Probably because I have 2 languages on my site?
      and in the pagecache.php the language is “en” and currency “USD” but I also have language “fr” and currency “CAD”

      Is it possible to put both of language and currency in the pagecache.php file?

      Regards
      Cleo

    • Cleo June 19, 2014 at 12:33 am ·

      Hi

      Ok just want to let you know that I found how to make it bilingual but finally I just deleted the page caching script because with it the affiliate link generator is not working, and maybe some other functions that I didn’t check yet but just in case it does I prefer not to use it.

      Thanks
      Cleo

  5. swed May 25, 2014 at 1:12 pm · Reply

    Gee MarketInSG, it’s so beautiful one could cry… and I second R Rogerson all the way. We’re selling seasonal commodities so during that time I’m constantly adding new products and I would like to see the changes immediately. Will your trick work for me @sfk?
    And MarketInSG! Thanks again. Stumbling over your tuts have been a very positive experience.

  6. R Rogerson May 13, 2014 at 11:42 am · Reply

    !!! WARNING !!!
    For those that haven’t realised it - this is a time-based cache.
    That means changes to products/categories may not appear “live” until the next caching occurs (the time elapses and a new cache-snapshot is taken).

    The only solutions are;
    1) You wait and let it happen (I think the 2400 = 40 minutes?)
    2) You manually clear your page-cache(s) so the new ones are generates
    3) You add in some code elsewhere to delete/create the cache on-change (such as when altering products/categories)

    But, for a “quick fix”, this is still fantastic!

  7. R Rogerson May 1, 2014 at 4:39 pm · Reply

    Fantastic job of a simple page cache!

    I would say “I wonder why they don’t include this sort of thing by default”,
    but after seeing how the owner and main Devs respond to people raising bugs etc., I’m not surprised such a simple, clever, useful and needed measure is Not included (they suck at implementing sensible ideas!).

    All I can say is “thank you” and “more more more” :D

  8. Adi January 29, 2014 at 9:41 am · Reply

    Hi,
    I’m getting this error in admin panel:

    Parse error: syntax error, unexpected ‘public’ (T_PUBLIC) in /home/zivaro/public_html/vqmod/vqcache/vq2-system_library_response.php on line 79

    This is line 79:

    }public function setOutput($output) {if (defined(‘CACHE_FILE’)) {$cacheFile = CACHE_FILE; $fp = gzopen($cacheFile, ‘w9′);gzwrite($fp, $output);gzclose($fp);}require_once(VQMod::modCheck(‘config.php’));require_once(VQMod::modCheck(‘system/library/pagecache.php’));

    • Adi January 29, 2014 at 4:02 pm ·

      Sorry, it was my mistake, i fix it and now it works like a charm.
      Thank you for share with us.
      Respect,
      Adi
      http://www.ziva.ro

  9. sfk January 29, 2014 at 8:56 am · Reply

    This is great! It reduce my site render time nearly 40% (from 17 mp 10 mp) ! But I must change .htaccess file and add to it this lines:
    # Turn on Expires and set default to 0
    ExpiresActive On
    ExpiresDefault A0

    # Set up caching on media files for 5 weeks

    ExpiresDefault A3024000
    Header append Cache-Control “public”

    # Set up caching on media files for 5 weeks

    ExpiresDefault A3024000
    Header append Cache-Control “public”

    # Set up 5 week caching on commonly updated files

    ExpiresDefault A3024000
    Header append Cache-Control “proxy-revalidate”

    # Force no caching for dynamic files

    ExpiresActive Off
    Header set Cache-Control “private, no-cache, no-store, proxy-revalidate, no-transform”
    Header set Pragma “no-cache”

  10. peter January 21, 2014 at 2:33 am · Reply

    Hi, a quick question. I following the instruction and am lost at this portion below. Where shall I include this part of the script? I put into the “public function setOutput($output) {” in the response.php and got an error.

    peace, peter.

    —————————————————————————————————————
    Inside the function, we will attempt to capture the data. We will first check if the cache file path has been defined.

    if (defined(‘CACHE_FILE’)) {
    Then, we will start working on it. We will write the contents into the file.

    $cacheFile = CACHE_FILE;

    $fp = gzopen($cacheFile, ‘w9′);
    gzwrite($fp, $output);
    gzclose($fp);
    }

    ————————————————————————————————-

  11. syed January 9, 2014 at 7:06 pm · Reply

    Man you are our opencart Saviour…

    Thank you so much buddy

  12. clt December 29, 2013 at 5:55 pm · Reply

    Hello,
    This script is one life saver. It worked very well.
    One question I have is, how can this handle when you have an update to a page.
    When you do price change or any instant changes to a page, it will pick up the
    cache that is without any of the changes made. Is there a way to add a time stamp check
    or something to verify the last modified from any of the tables….
    Hope some one will get back with me on this…
    Thanks,

  13. David Sims September 11, 2013 at 12:56 pm · Reply

    I tried but get an error:

    2013-09-11 13:35:15 - PHP Warning: Cannot modify header information - headers already sent by (output started at /var/sites/m/maycontainmutts.com/public_html/system/library/pagecache.php:1) in /var/sites/m/maycontainmutts.com/public_html/system/library/currency.php on line 45

    using 1.5.51

    • David Sims September 11, 2013 at 1:27 pm ·

      OK - very strange using Filezilla FTP I had to set ASCII mode transfer and the error is fixed

  14. netrat September 9, 2013 at 8:31 pm · Reply

    Very nice, works great, except that my site is in Cyrillic with utf8, and when I write the cache files, the Cyrillic encoding goes bad. Should I encode $output, before write it into the file?

    • netrat September 9, 2013 at 8:40 pm ·

      OK I found it by my self.
      I change “gzwrite($fp, $output);”
      to fwrite($fp, “\xEF\xBB\xBF” . $output);
      and the magic works.
      Thanks for the great work!

  15. MarketInSG May 7, 2013 at 5:58 am · Reply

    all versions.

  16. Agung May 7, 2013 at 5:33 am · Reply

    This tutorial for Opencart 1551? or All version? Thank

    • R.C September 18, 2013 at 11:22 pm ·

      Yes it works for 1.5.5.1. I guess it works for every version.
      I have a shop with over 400 000 (yes 400K) categories and this helped a lot, though Opencart sucks at mysql queries, you can see that if you add many categories and products.

Leave a Comment

comm comm comm