Cache it! Solve PHP Performance Problems Article

Share this article

In the good old days when building web sites was as easy as knocking up a few HTML pages, the delivery of a web page to a browser was a simple matter of having the web server fetch a file. A site’s visitors would see its small, text-only pages almost immediately, unless they were using particularly slow modems. Once the page was downloaded, the browser would cache it somewhere on the local computer so that, should the page be requested again, after performing a quick check with the server to ensure the page hadn’t been updated, the browser could display the locally cached version. Pages were served as quickly and efficiently as possible, and everyone was happy.
Then dynamic web pages came along and spoiled the party by introducing two problems:
  • When a request for a dynamic web page is received by the server, some intermediate processing must be completed, such as the execution of scripts by the PHP engine. This processing introduces a delay before the web server begins to deliver the output to the browser. This may not be a significant delay where simple PHP scripts are concerned, but for a more complex application, the PHP engine may have a lot of work to do before the page is finally ready for delivery. This extra work results in a noticeable time lag between the user’s requests and the actual display of pages in the browser.
  • A typical web server, such as Apache, uses the time of file modification to inform a web browser of a requested page’s age, allowing the browser to take appropriate caching action. With dynamic web pages, the actual PHP script may change only occasionally; meanwhile, the content it displays, which is often fetched from a database, will change frequently. The web server has no way of discerning updates to the database, so it doesn’t send a last modified date. If the client (that is, the user’s browser) has no indication of how long the data will remain valid, it will take a guess. This is problematic if the browser decides to use a locally cached version of the page which is now out of date, or if the browser decides to request from the server a fresh copy of the page, which actually has no new content, making the request redundant. The web server will always respond with a freshly constructed version of the page, regardless of whether or not the data in the database has actually changed.
To avoid the possibility of a web site visitor viewing out-of-date content, most web developers use a meta tag or HTTP headers to tell the browser never to use a cached version of the page. However, this negates the web browser’s natural ability to cache web pages, and entails some serious disadvantages. For example, the content delivered by a dynamic page may only change once a day, so there’s certainly a benefit to be gained by having the browser cache a page–even if only for 24 hours. If you’re working with a small PHP application, it’s usually possible to live with both issues. But as your site increases in complexity–and attracts more traffic–you’ll begin to run into performance problems. Both these issues can be solved, however: the first with server-side caching; the second, by taking control of client-side caching from within your application. The exact approach you use to solve these problems will depend on your application, but in this chapter, we’ll consider both PHP and a number of class libraries from PEAR as possible panaceas for your web page woes. Note that in this chapter’s discussions of caching, we’ll look at only those solutions that can be implemented in PHP. For a more general introduction, the definitive discussion of web caching is represented by Mark Nottingham’s tutorial.
Furthermore, the solutions in this chapter should not be confused with some of the script caching solutions that work on the basis of optimizing and caching compiled PHP scripts, such as Zend Accelerator and ionCube PHP Accelerator. This chapter is excerpted from The PHP Anthology: 101 Essential Tips, Tricks & Hacks, 2nd Edition.
How do I prevent web browsers from caching a page?
If timely information is crucial to your web site and you wish to prevent out-of-date content from ever being visible, you need to understand how to prevent web browsers–and proxy servers–from caching pages in the first place. Solutions There are two possible approaches we could take to solving this problem: using HTML meta tags, and using HTTP headers. Using HTML Meta Tags The most basic approach to the prevention of page caching is one that utilizes HTML meta tags: <meta http-equiv="expires" content="Mon, 26 Jul 1997 05:00:00 GMT"/> <meta http-equiv="pragma" content="no-cache" /> The insertion of a date that’s already passed into the Expires meta tag tells the browser that the cached copy of the page is always out of date. Upon encountering this tag, the browser usually won’t cache the page. Although the Pragma: no-cache meta tag isn’t guaranteed, it’s a fairly well-supported convention that most web browsers follow. However, the two issues associated with this approach, which we’ll discuss below, may prompt you to look at the alternative solution. Using HTTP Headers
A better approach is to use the HTTP protocol itself, with the help of PHP’s header function, to produce the equivalent of the two HTML meta tags above: <?php header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); ?> We can go one step further than this, using the Cache-Control header that’s supported by HTTP 1.1-capable browsers: <?php header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', FALSE); header('Pragma: no-cache'); ?> For a precise description of HTTP 1.1 Cache-Control headers, have a look at the W3C’s HTTP 1.1 RFC. Another great source of information about HTTP headers, which can be applied readily to PHP, is mod_perl’s documentation on issuing correct headers. Discussion Using the Expires meta tag sounds like a good approach, but two problems are associated with it:
  • The browser first has to download the page in order to read the meta tags. If a tag wasn’t present when the page was first requested by a browser, the browser will remain blissfully ignorant and keep its cached copy of the original.
  • Proxy servers that cache web pages, such as those common to ISPs, generally won’t read the HTML documents themselves. A web browser might know that it shouldn’t cache the page, but the proxy server between the browser and the web server probably doesn’t–it will continue to deliver the same out-of-date page to the client.
On the other hand, using the HTTP protocol to prevent page caching essentially guarantees that no web browser or intervening proxy server will cache the page, so visitors will always receive the latest content. In fact, the first header should accomplish this on its own; this is the best way to ensure a page is not cached. The Cache-Control and Pragma headers are added for some degree of insurance. Although they don’t work on all browsers or proxies, the Cache-Control and Pragma headers will catch some cases in which the Expires header doesn’t work as intended–if the client computer’s date is set incorrectly, for example. Of course, to disallow caching entirely introduces the problems we discussed at the start of this chapter: it negates the web browser’s natural ability to cache pages, and can create unnecessary overhead, as new versions of pages are always requested, even though those pages may not have been updated since the browser’s last request. We’ll look at the solution to these issues in just a moment.
How do I control client-side caching?
We addressed the task of disabling client-side caching in “How do I prevent web browsers from caching a page?”, but disabling the cache is rarely the only (or best) option. Here we’ll look at a mechanism that allows us to take advantage of client-side caches in a way that can be controlled from within a PHP script. Apache Required! This approach will only work if you’re running PHP as an Apache web server module, because it requires use of the function getallheaders–which only works with Apache–to fetch the HTTP headers sent by a web browser. Solutions In controlling client-side caching you have two alternatives. You can set a date on which the page will expire, or respond to the browser’s request headers. Let’s see how each of these tactics is executed. Setting a Page Expiry Header The header that’s easiest to implement is the Expires header–we use it to set a date on which the page will expire, and until that time, web browsers are allowed to use a cached version of the page. Here’s an example of this header at work: expires.php (excerpt)

<?php
function setExpires($expires) {
header(
'Expires: '.gmdate('D, d M Y H:i:s', time()+$expires).'GMT');
}
setExpires(10);
echo ( 'This page will self destruct in 10 seconds<br />' );
echo ( 'The GMT is now '.gmdate('H:i:s').'<br />' );
echo ( '<a href="'.$_SERVER['PHP_SELF'].'">View Again</a><br />' );
?>
In this example, we created a custom function called setExpires that sets the HTTP Expires header to a point in the future, defined in seconds. The output of the above example shows the current time in GMT, and provides a link that allows us to view the page again. If we follow this link, we’ll notice the time updates only once every ten seconds. If you like, you can also experiment by using your browser’s Refresh button to tell the browser to refresh the cache, and watching what happens to the displayed date.
Go to page: 1 | 2 | 3 | 4 | 5

Frequently Asked Questions about PHP Caching and Performance

What is PHP caching and why is it important?

PHP caching is a process that stores the output of PHP code in a temporary location, so that when the same information is needed again, it can be retrieved much faster. This is important because it significantly improves the performance and speed of a website. Without caching, the server would have to execute the same PHP code and query the database every time a user visits your website, which can slow down the site and consume more server resources.

How does PHP caching work?

PHP caching works by storing the results of PHP code execution in a cache. When a user requests a page, the server first checks if a cached version of the page exists. If it does, the server delivers the cached version, which is much faster than executing the PHP code again. If a cached version does not exist, the server executes the PHP code, generates the page, and stores the result in the cache for future use.

What are the different types of PHP caching?

There are several types of PHP caching, including OpCode caching, Object caching, and Page caching. OpCode caching stores precompiled script bytecode, so the PHP interpreter doesn’t need to parse and compile the script each time. Object caching stores database query results, reducing the need for repeated database queries. Page caching stores the entire HTML output of a page, eliminating the need for PHP code execution and database queries on subsequent visits.

How can I implement PHP caching on my website?

Implementing PHP caching on your website can be done using various methods. One of the most common ways is by using a PHP caching extension like APC or OPcache. These extensions can be installed and enabled on your server, and they automatically cache the PHP scripts your website runs. Another method is to use a caching plugin if your website is built with a CMS like WordPress.

What is the difference between client-side and server-side caching?

Client-side caching involves storing web page resources on the user’s device, so they don’t have to be downloaded again on subsequent visits. This includes files like HTML, CSS, JavaScript, and images. Server-side caching, on the other hand, involves storing the results of server-side computations, like PHP code execution or database queries. This reduces the server’s workload and improves website speed.

How can I control the caching behavior of my PHP website?

You can control the caching behavior of your PHP website by sending specific HTTP headers with your responses. These headers can instruct the client’s browser to cache certain resources, or not to cache at all. You can also set the maximum age for cached resources, after which the browser should request a fresh copy from the server.

Can PHP caching cause any issues?

While PHP caching generally improves website performance, it can sometimes cause issues. For example, if a cached version of a page is served to the user, any recent changes to the page might not be reflected. This can be mitigated by implementing a cache invalidation strategy, which clears the cache whenever the underlying data changes.

What is cache invalidation and how does it work?

Cache invalidation is a process that removes entries from the cache when the underlying data changes, ensuring that users always see the most up-to-date content. This can be done manually, by calling a function to clear the cache, or automatically, by setting a time-to-live (TTL) value for each cache entry.

How can I measure the performance improvement from PHP caching?

You can measure the performance improvement from PHP caching by using tools like Google PageSpeed Insights or GTmetrix. These tools provide a before-and-after comparison of your website’s loading speed. You can also use server monitoring tools to track the server’s resource usage.

Are there any best practices for PHP caching?

Some best practices for PHP caching include: using a suitable caching strategy for your website’s needs, regularly testing your website’s performance, implementing a cache invalidation strategy, and keeping your caching tools and extensions up-to-date. It’s also important to remember that caching is not a substitute for efficient and optimized code.

Ben DechraiBen Dechrai
View Author

Ben Dechrai was born in Germany, grew up in the UK, lives in Melbourne, and brews his own beer. A software developer and open source community liaison by day, Ben is Treasurer of the Open Source Developers' Club and convener of the Melbourne PHP Users Group and BarCampMelbourne. He frequently speaks at Australian and international conferences and events on a broad range of topics. He also drinks a lot of coffee.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week