PHP - Caching Pages with Output Buffering
Join the DZone community and get the full member experience.
Join For FreeNoticing your pages are loading slowly or just don't like using extra cpu cycles when you don't have to? PHP makes caching very easy with their variety of Output Control Functions. In this article I'll go over complete page caching which is the easiest to implement and understand.
One of the reasons I love PHP is because there is a function for everything. And if there's not then it's not hard to find someone who has one created for you. Failing that, PHP has probably given you a great head-start in writing your own. This is the case with PHP's Output Control Functions. Some people would argue this is a weakness in PHP but that's another article.
The concept behind page-caching is very simple to understand. The whole idea behind it is to take a generated page that performs many operations such as database calls, looping, etc and store the output somewhere like the file system. This way, the next time someone requests the same page we are able to grab and display the output without having to go through all the trouble of making the same database queries and calculations over and over again.
Output Buffering Overview
Understanding how Output Buffering works is reasonable simple. There are 3 basic functions you will want to get yourself familiar with:
<?php
ob_start(); // Turns on output buffering
?>
From the moment you call ob_start() any output will be saved in PHP's internal buffer and not yet printed to the screen. This includes HTML and echoed or printed php statements. Header statements are the exception as they are still sent to the browser.
<?php
// Stores the contents of the buffer in a variable as a string
$contents = ob_get_contents();
?>
ob_get_contents() will return a string value of the current contents of the buffer. This will prove very useful for caching purposes when placed at the end of pages. More on that in a bit.
<?php
ob_end_flush(); // Turn off buffering and print the contents
?>
ob_end_flush() will print all the contents of the buffer just as you would expect to see it if output buffering was never turned on.
Caching Pages
You can probably already guess how to use these 3 functions to create some page caching functionality, as all the basics have been introduced, but there are a few extra things that haven't been covered yet. Caching pages is usually divided into 2 blocks of code. The larger of which is at the very beginning of each page which sets some variables, does some checking, and decides what the best course of action to take is. The latter is at the very end of a page and saves any content deemed ok to cache to the file system.
That's another thing, for the purposes of this article I am saving all cached content to the file system but this could easily be converted to a database or some other form a storage. For now, let's keep it simple.
So let's get started with the first block of code to be placed at the top of the page:
<?php
$cache_time = 3600; // Time in seconds to keep a page cached
$cache_folder = '/cache'; // Folder to store cached files (no trailing slash)
$cache_filename = $cache_folder.md5($_SERVER['REQUEST_URI']); // Location to lookup or store cached file
//Check to see if this file has already been cached
// If it has get and store the file creation time
$cache_created = (file_exists($cache_file_name)) ? filemtime($this->filename) : 0;
if ((time() - $cache_created) < $cache_time) {
readfile($cache_filename); // The cached copy is still valid, read it into the output buffer
die();
}
?>
Quite a few things going on here. The first block of code is simply a set of configuration variables.
$cache_folder may have to use relative pathing as not all web hosts honor the document root.
$_SERVER['REQUEST_URI'] is simply the requested page and any parameters that were sent. We md5 this to ensure a unique file name. An example of the string returned from $_SERVER['REQUEST_URI']:
<?php
// Let's say our current URL is http://www.domain.com/directory/file.php?id=42
echo $_SERVER[REQUEST_URI]; // prints '/directory/file.php?id=42'
?>
$cache_created is set to the time (UNIX timestamp) the last cache file for this page was created. If this fails, set it to 0.
The IF statement checks to see if the cached file is still within the set time limit, is true then grab the cached contents, store them in the buffer and exit the page. exit() and die() automatically call ob_end_flush() so the buffered output is automatically printed. If a file is not found or the time has expired start output buffering using ob_start();
From this point on let's assume that we plan on caching the page. Anything after this initial block of code will be stored in the output buffer. The last step is to add some code to the very end of this file to store the buffered content in a cache file to be used the next time someone loads this page.
<?php
file_put_contents($cache_filename, ob_get_contents());
ob_end_flush();
?>
Short and sweet. At this point we already know the only way to get this far into the page is if all the other caching requirements have failed and we need to save a new copy to the file system. We do that simply by calling file_put_contents() and storing the contents of the buffer via ob_get_contents().
Things to Consider
Before you go off caching your entire site it's important to think about which pages you want to apply this method to. Static HTML pages are probably not going to see any performance boost since there are no calculations or database calls to be made and in fact will probably slow things down because the PHP interpreter will have to be invoked. PHP-Driven List and Detail pages where content, for the most part, stays the same but is being assembled each time from a database are ideal for caching.
For example, if you run a blog and you want to cache your main page that lists your newest entries this is probably a great idea. However, when you post a new story you may not see it appear on the front-page right away. To circumvent this just delete all the files in your cache directory.
A page I would not use this own is one that is updated frequently and time is of the essence. For example, it's a horrific idea to cache a page listing stock prices to an audience of stock brokers where every second counts. Another example of when not to cache a page is if content is taylored to a certain user. A dashboard for example is meant to have up to the minute information and only a few users are going to be requesting it.
There are of course exceptions to this rule and many more scenarios but too many to cover here. Just use a little common sense and think things through.
Published at DZone with permission of Michael Bernat, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments