Output Caching with PHP

Dynamic Web pages take longer to load than static ones, which forces visitors to your site to wait — and we all know they won’t wait for very long. Output caching is a powerful technique you can use to shorten that wait and keep them from leaving.

If we take a nostalgic look at those distant days when developing Websites consisted of writing a few, static HTML pages, made up mostly of text and scarce images, we notice that the process of sending a Web page from the Web server to a Web browser was painless and straightforward. The server had to fetch some files from the file system, and send them happily to the user’s browser, which received the parsed page almost immediately.

Once the page was displayed, the browser would trigger its caching routines to store the finished page on the user’s computer, until a new request was made for that page. With a new request for that page, the browser performed a fast check with the Web server to make sure the page hadn’t been updated, and then could display the cached, locally stored version. Ah, memories! 

As you know, the Web became more and more complex, and new techniques had to be developed for delivering dynamic content. Most  websites migrated from static to dynamic schemas, according to current demands for satisfying content-hungry visitors. This new context rapidly broke the well-established process described above.

Now, nearly every page requested puts into action some kind of intermediate process (or many of them) that introduces a noticeable delay when delivering Web pages to the end user. If you are within the field of PHP processing, you know that the Web server will call the PHP engine for parsing scripts, and maybe those scripts will connect to databases, and so on, all of this resulting in a lag between the user’s request for a page and the final display in the browser.

If keeping our applications simple is not mandatory, they will become more complex, and easily cause us to run into difficulties that could increase the lag problem. There are many possible approaches to solve this situation. In this article, we will see how server side caching can help us to implement simple but powerful PHP mechanisms to avoid, or reduce, the delay between a Web server sending a dynamic page and the user seeing the finished result displayed on the machine.

{mospagebreak title=Capturing Server Side Output}

Now, with the preliminaries out of the way, let’s see how server side delay can be reduced using PHP. We will generate a normal PHP page, maybe retrieving records from a database, performing others tasks needed for the page, and so on. However, before sending the final rendered page to the browser, we will need to capture that output and store it in a file (the cache file). The next time the page is requested, we’ll find out whether there is a cached version of it (this means checking to see if the cache file exists).

If there is a cache file, instead of rebuilding the entire page again, we’ll just read the contents from the file and send them straight to the browser. This will noticeably reduce the time taken to redisplay the page. In order to catch the server output, we will utilize some of the buffer control functions that PHP offers for storing data in a buffer created in server memory. Buffer control functions (when used properly) offer a great mechanism for controling server output, whether output is cached or processed in different ways (for instance, when building pages from template systems).

Here is a simple example of output buffering:

<?php
ob_start();
   // start an output buffer
echo ‘This text is in the buffer!<br />’;
   // echo some output that will be stored in the buffer
$bufferContent = ob_get_contents();
   // store buffer content in a variable
ob_end_clean();
   //   stop and clean the output buffer
echo ‘This text is not in the buffer!<br />’;
   //   echo text normally
echo $bufferContent;
   //   echo the buffer content
?>

As we can see clearly, the above script starts output buffering with the function ob_start. Then it uses echo to display some text, which is stored in the buffer. Next, it retrieves the buffer content and stores it in a variable ($bufferContent). The ob_end_clean function stops output buffering and cleans the buffer. After that, some text is normally echoed to the browser and finally the buffer content is displayed.

The script output is the following:

This text is not in the buffer!
This text is in the buffer!

This simplistic example shows us how to capture content from the output buffer, process it in some way (for caching purposes, for example), and finally display output in the browser.

Think about how powerful this technique could be when building websites. It’s not only possible to manipulate data for caching, but also for filling data into templates placeholders, or to trigger error handling processes, among others advantages. Because we are manipulating data without sending anything to the user’s browser, we can programmatically handle any output, hiding the intermediate processes from the visitor, and behaving according to our application logic.

{mospagebreak title=Output Buffering for Server Side Caching}

Now, with this example in our hands, let’s see how we can use output buffering for server side caching. Let’s implement a very simple demonstration, where the buffer content is stored as a file:


<?php

//   check if there is a cached version
if ( file_exists( ‘cachefile.txt’ ) ) {
   //   if there is a cached version read content and display
  readfile ( ‘cachefile.txt’ );
  exit();
}

//   if there is not a cached version start output buffering
ob_start();

//   display some HTML ( this will be stored in the buffer )
?>

<html>
<head>
<title>Caching server output</title>
</head>
<body>
<h2>This page is a cached Page</h2>
</body>
</html>

<?php
$bufferContent = ob_get_contents();
   //   get buffer content
ob_end_flush();
   //   clean and display buffer content in the browser
$fp = fopen ( ‘cachefile.txt’ , ‘w’ ) or die ( ‘Error opening cache file’ );
   //  write buffer content to cache file
fwrite ( $fp , $bufferContent );
fclose( $fp );
?>

The output for the above script is the parsed HTML:

This page is a cached Page

The contents of the cache file ‘cachefile.txt’ is the same HTML included in the script:

<html>
<head>
<title>Caching server output</title>
</head>
<body>
<h2>This page is a cached Page</h2>
</body>
</html>

Let’s examine in detail what’s going on within the script:

First, the script checks to see if there is a cached version of the page, by determining if a cache file exists. If it does, it then reads the cache file and send the output to the browser. Otherwise, it starts a new output buffer and store some HTML on it to generate the cached version. Once this is done, it gets the buffer content and stores it in a variable ($bufferContent). Next, the buffer is cleared and the HTML is displayed to the user. Finally, the buffer content is written to the cache file.

Obviously, this script is very simple, but exposes in a nutshell the powerful caching capabilities that PHP has built-in, when used in conjunction with output buffering functions. Having all of this potential in our hands, the logical next step is caching different sections of a Web page. Web pages usually have a header section, body and a footer section. It would be good to apply these caching techniques accordingly to how frequently those generic sections are updated.  We’re moving that way now.

{mospagebreak title=Multiple caching: splitting the Web page content}

As stated above, since most websites are composed of a header section, body and footer section, we are going to implement server side caching for each part. Also, we need to set up some kind of criteria for checking whether cache files are valid. The most common caching policy approaches are the following:

  • Time triggered caching (expiry timestamp).

  • Content change triggered caching (sensitive content has changed, so cache must be updated).

  • Manually triggered caching (manually inform the application that information is outdated, and force a new cache creation).

For the scope of this article, we will use a time triggered caching policy for each part of the web page.

Let’s see how it works:

First, we define that the page header will have an expiry time of one day or 86400 seconds (only for purposes of example). Our body section will have an expiry time of ten seconds, and our page footer will be valid for one day.

Having defined expiry times for each section, let’s build two very intuitive and useful functions. The first one will take care of creating the cache files for the proper section. The second will check to see whether there is a valid cached version for the section. If there is, it will return the cached content extracted from the cache file. Otherwise, it will return false.

The functions are as follow:

function createCache ( $content ,  $cacheFile ) {
  $fp = fopen( $cachefile , ‘w’ );
  fwrite( $fp , $content );
  fclose( $fp);
}

function getCache ( $cacheFile ,  $expireTime ) {
  if ( file_exists ( $cacheFile ) && filemtime ( $cacheFile ) >( time() – $expireTime ) ) {
    return file_get_contents( $cacheFile );
  }
  return false;
}

The function createCache takes two parameters: content from the output buffer and the cache filename where the output will be stored. Once the buffer content is obtained, it is stored as a file. Similarly, the function getCache accepts two arguments: the cache filename and expiry time (expressed in seconds). The function checks to see whether there is a cache file and whether it is valid. Cache validity is tested to see whether the cache file modification timestamp is greater than the current time less the expiry time. If the cache file is found to be valid, then cache content is returned. Otherwise it returns false.

Having defined this two core functions, we are able to build the new script:

<?php
//   start output buffering
ob_start();

//  check if a valid header cache exists
if ( !$header = getCache( ‘headerCache.txt’ , 86400 ) {

//  display header section
?>

<html>
<head>
<title>Cached Page</title>
</head>
<body>
The header section is updated on a daily basis.

<?php
$header = ob_get_contents();
ob_clean();
createCache( $header , ‘headerCache.txt’ );

}

// check if a valid body cache exists
if ( !$body = getCache( ‘bodyCache.txt’ , 10 ) {

//  display body section
?>

<h1>This section is updated every 10 seconds.</h1>

<?php
$body = ob_get_contents();
ob_clean();
createCache( $body , ‘bodyCache.txt’ );

}

// check if a valid footer cache exists
if ( !$footer = getCache( ‘footerCache.txt’ , 86400 ) {

//  display footer section
?>
The footer section is updated on a daily basis.
</body>
</html>

<?php
$footer = ob_get_contents();
ob_clean();
createCache( $footer , ‘footerCache.txt’ );

}

//   stops output buffering
ob_end_clean();

//   display the complete page

echo $header .  $body .  $footer;

?>

The script is pretty straightforward in its functionality. Let’s see what it does in detail.

Once output buffering is started, the script checks in turn for the corresponding cache files, which store header, body and footer sections. If the cache files are not valid based on expiry times given for each section, new content is generated, stored in the buffer and finally written to the proper cache files. When the whole page content is obtained either from the buffer or from cache files, it will be displayed in the user’s browser.

The script’s output is the following:

The header section is updated on a daily basis.

This section is updated every 10 seconds.

The footer section is updated on a daily basis.

Each section of the page will be updated according to our caching policy, with the body updated every ten seconds.

{mospagebreak title=Putting it All Together}

Here is the complete script:

<?php

//  define createCache
function createCache ( $content ,  $cacheFile ) {
  $fp = fopen( $cachefile , ‘w’ );
  fwrite( $fp , $content );
  fclose( $fp);
}

// define getCache
function getCache ( $cacheFile ,  $expireTime ) {
  if ( file_exists ( $cacheFile ) && filemtime ( $cacheFile ) >( time() – $expireTime ) ) {
    return file_get_contents( $cacheFile );
  }
  return false;
}

//   start output buffering
ob_start();

//  check if a valid header cache exists
if ( !$header = getCache( ‘headerCache.txt’ , 86400 ) {

//  display header section

?>

<html>
<head>
<title>Cached Page</title>
</head>
<body>
The header section is updated on a daily basis.

<?php
$header = ob_get_contents();
ob_clean();
createCache( $header , ‘headerCache.txt’ );

}

// check if a valid body cache exists
if ( !$body = getCache( ‘bodyCache.txt’ , 10 ) {

//  display body section
?>

<h1>This section is updated every 10 seconds.</h1>


<?php
$body = ob_get_contents();
ob_clean();
createCache( $body , ‘bodyCache.txt’ );

}

// check if a valid footer cache exists
if ( !$footer = getCache( ‘footerCache.txt’ , 86400 ) {

//  display footer section
?>
The footer section is updated on a daily basis.
</body>
</html>

<?php
$footer = ob_get_contents();
ob_clean();
createCache( $footer , ‘footerCache.txt’ );

}

//   stops output buffering
ob_end_clean();

//   display the complete page

echo $header .  $body .  $footer;

?>

Conclusion

That’s the general idea behind the concept of caching server side output. Major performance improvements can be achieved if used in conjunction with proper caching policies for specific sections of the website.

Since I am a strong advocate of object oriented programming, I would recommend using some good and trusted caching classes, such as Pear::Cache_Lite, in order keep your code maintainable and have a reliable caching mechanism for websites.

From this point, there is long way to go. Caching is a very huge subject, and it can be approached from several points. But one thing is certain: caching server output with PHP output buffering functions is a good addition to your toolbox when building dynamic websites. Good luck!

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye