In this article I'll show you a simple approach to enable your Zend Framework application saving lots of precious bandwidth, and thus, making it more end-users friendly, and save on bandwidth costs.
This technique involves HTTP conditional GET. This is basically a feature of the HTTP protocol. By sending correct HTTP headers with your application, you enable browsers of your end users to cache pages of your site.
Are you worried about users having old versions of page in cache? Then don't! This technique allows to get all the benefits of client side caching without affecting anything but 5 minutes of your time to integrate it.
The Zend Framework is great in regards that you can easily extend it. We are going to create a front controller plugin, which will take care of handling conditional GET requests.
Let's create our front controller plugin:
-
<?php -
/** -
* Plugin to support conditional GET for php pages (using ETag) -
* Should be loaded the very last in the plugins stack -
* -
* @author $Author: danila $ -
* @version $Id: Conditional.php 15741 2009-02-08 11:58:44Z danila $ -
* -
*/ -
class Smartycode_Http_Conditional extends Zend_Controller_Plugin_Abstract
-
{ -
-
public function dispatchLoopShutdown()
-
{ -
$send_body = true;
-
-
$etag = '"' . md5($this->getResponse()->getBody()) . '"';
-
-
$inm = split(',', getenv("HTTP_IF_NONE_MATCH"));
-
-
$inm = str_replace('-gzip', '', $inm);
-
-
// TODO If the request would, without the If-None-Match header field, -
// result in anything other than a 2xx or 304 status, -
// then the If-None-Match header MUST be ignored -
-
foreach ($inm as $i) {
-
if (trim($i) == $etag) {
-
$this->getResponse()
-
->clearAllHeaders()
-
->setHttpResponseCode(304)
-
->clearBody();
-
$send_body = false;
-
break;
-
} -
} -
-
$this->getResponse()
-
->setHeader('Cache-Control', 'max-age=7200, must-revalidate', true)
-
->setHeader('Expires', gmdate('D, d M Y H:i:s', time() + 2 * 3600) . ' GMT', true)
-
->clearRawHeaders();
-
-
if ($send_body) {
-
$this->getResponse()
-
->setHeader('Content-Length', strlen($this->getResponse()->getBody()));
-
} -
-
$this->getResponse()->setHeader('ETag', $etag, true);
-
$this->getResponse()->setHeader('Pragma', '');
-
-
} -
}
You may wish to adjust the "7200" value to something lot longer. This is the number of seconds that you allow to user's browser store cached page. Even "720000" is safe, because with the no-cache header browser "is told" to cache page (don't mind it says no-cache) and check with the freshness of it with your server.
Attaching this plugin to front controller is really easy. As simple as placing the following in your bootstrap file:
$frontController->registerPlugin(new Scafe_Http_Conditional(), 101);
Note the "101". You should register the plugin the very last in plugins' stack.
This simple steps just made your Zend Framework application more environment friendly:
- Works with AJAX calls going through Zend's MVC (all request types)
- Pages are not transmitted if they haven't changed since last time they were requested
- You can also assume you get the benefit for SEO, as search engines supporting Etag, will effectively be able to skip pages from downloading/reanalyzing and index your pages more quickly.
- Sends Content-Length header, enabling Keep-Alive connections
- There are more benefits, I'm lazy to think about all of them
http://smartycode.com/performance/zend-framework-browser-caching/