Improve Zend Framework application speed by separate static content and using Zend_Cache and Zend_View for static files

Zend Framework Speed UP

Introduction

Why doing this, the reason is simple. We dont need to process all static files in our framework, because this increase loading time of your application. The ideea is to create a separate domain for your application eg. http://s.domain.com wich will render and cache all requested files. I personally used this method and my application wich is based on Zend Framework is considerably faster. The Zend_View is used to merge multiple css or js files and pass parameters. Also compressing and obfuscation classes are used to decrease file sizes.

Create .htaccess

For beggining a .htaccess file is created for redirecting all request to render file. The files that is redirected to the render file is just js and css files, the rest of them like pictures, zip and other binary files are loaded from normal path. Also if apache deflate  module is available we add filters for css,xml and javascript files to compress files.

AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript

RewriteEngine on RewriteRule !\.(swf|xml|jpg|png|PNG|JPG|gif|otf|ttf|htc|ico)$ render.php
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ render.php [NC,L]

Render file

This is the file called render.php wich process all requests sepcified by .htaccess file. For example if request is /css/common.css  this file is loaded into Zend_View and rendered. If the request has some parameters like /css/common.css?foo=1&bar=1 the request is parsed with php function parse_str and passed to the Zend_View.  Before the file is sent to output the content is compressed with specific class, for css i personally used  the CSSCompressor by Stephen Clay and for javascript files JSMinPlus by Tino Zijdel. After the file is compressed the content is cached by Zend_Cache with the md5 of request like id. And finaly send the cache headers for browsers and print it to ouput.

<!?php 
error_reporting(E_ALL); 
ini_set('display_errors','1'); 
set_include_path(implode(PATH_SEPARATOR, array(     dirname(__FILE__).'/library',     get_include_path(), ))); 

require_once "Zend/Uri.php"; 
require_once "Zend/Cache.php"; 
require_once "Zend/View.php"; 
require_once 'functions.php'; 

define('CACHE_DAYS', 30); 
define('USE_CACHE', false); 
define('USE_COMPRESSOR', false); 

$request_file = $_SERVER['REQUEST_URI']; 

if($request_file == "/" || empty($request_file)) 
{  notFound(); } 

$cache_id = md5($request_file); 
$uri = Zend_Uri::factory("http://s.domain.com".$request_file); 
parse_str($uri->getQuery(), $uri_params);

$file_parts = explode('.', basename($uri->getPath()));

$extension = $file_parts[ count($file_parts) - 1 ];

$file = basename($uri->getPath());

$file_path = str_replace('\\','/',dirname(__FILE__) . $uri->getPath());

$frontendOptions = array(
   'lifetime' => 3600 * 24 * CACHE_DAYS, // cache lifetime of 2 hours
   'automatic_serialization' => true
);

$backendOptions = array(
	'cache_dir' => dirname(__FILE__).'/cache/' // Directory where to put the cache files
);

// getting a Zend_Cache_Core object
$cache = Zend_Cache::factory('Core',
							 'File',
							 $frontendOptions,
							 $backendOptions);

if(file_exists($file_path) && is_file($file_path))
{
	switch ( $extension )
	{
	    case "css":
	        header("Content-Type: text/css");

			if(USE_COMPRESSOR)
	        require_once "CSSCompressor.php";

			sendHeaders($file_path);

			if( ($result = $cache->load($cache_id)) === false )
			{
				$result = renderView($file_path, $uri_params);

				if(USE_COMPRESSOR)
				$result = CSSCompressor::process($result);

				$result = appendCacheInfo($result);

				if(USE_CACHE)
				$cache->save($result, $cache_id);
			}

			echo $result;

	        break;

	    case "js":
	        header("Content-Type: text/javascript");

			if(USE_COMPRESSOR)
	        require_once "JSMin.php";

			sendHeaders($file_path);

			if( ($result = $cache->load($cache_id)) === false )
			{
				 $result = renderView($file_path, $uri_params);

				 if(USE_COMPRESSOR)
				 $result = JSMin::minify($result);
				 $result = appendCacheInfo($result);

				 if(USE_CACHE)
				 $cache->save($result, $cache_id);
			}
	        echo $result;
	        break;
	    default:
	    	notFound();
	    	break;
	}
}
else
{
	notFound();
}

This steps considerably increase the time of loading of your application. The compressing calsses make files smaller with 20%. Also the big advantage for this is you can use the css and javascript files like php. For example if you want to merge multiple common files into one file, simply use render function from Zend_View

@CHARSET "UTF-8";
<?php echo $this--->render("reset.css"); ?>
<?php echo $this--->render("grid.css"); ?>
<?php echo $this--->render("layout.css"); ?>

This method is very usefully because reduce the number of files loaded trought the network.

To make sure the files are not requested every time and the client browser do its job, we send the caching headers using the below sendHeaders function.

function sendHeaders($file)
{
	$cache_days = CACHE_DAYS;

	if(file_exists($file))
	{
		$filetime = filemtime($file);

		$last_modified = date('D, d M Y H:i:s', $filetime) . ' GMT';
		$expires       = date('D, d M Y H:i:s', strtotime('+30 days',$filetime)) . ' GMT';
		$hasid         = '"' . md5($last_modified) . '"';

		header("Cache-Control:max-age=".($cache_days * 24 * 60 * 60));
		header("Pragma:private");
		header("Last-Modified:".$last_modified);
		header("Expires:".$expires);
		header("Vary: Accept-Encoding");
		header("Etag:".$hasid);

		$PageWasUpdated = !(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) and
			strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified);

		$DoIDsMatch = (isset($_SERVER['HTTP_IF_NONE_MATCH']) and
			@ereg($hasid, $_SERVER['HTTP_IF_NONE_MATCH']));

		// Does one of the two ways apply?
		if (!$PageWasUpdated or $DoIDsMatch)
		{
			header('HTTP/1.1 304 Not Modified');
			header('Connection: close');
			exit(1);
		}
	}

}

To work this function you must have some modules activated in your apache server. The modules are headers_module, expires_module.

Static files rendered like views

With your css and javascript files you cand do alots of things. In fact in your .css and .js files you cand write php code, for eg:

If you request: /js/account.js?logged=1 from the view file you can do somethink like this:

function onSomeButtonClick()
{
var logged = <?php echo ($this--->logged=="1") ? 'true' : 'false'; ?>;
if(logged) {
//do something
} else {
//do something
}
}

Conclusion

For my application this method is very fast and with alots of advantages and functionalities. The filesize is smaler using the compressing classes, also the apache is compress it with defalate module. With this positive points also have some issues, if you what to use the params sent to the view every time you must to disable cache.

2 Comments

  1. hi, i recently read this and very interested and then i have tried that myself i end up getting error message.

    However, at this time I do not trust myself to screw up, it is possible for you to fix like this to load faster!

    I’m using social network using zend framework, and that social network works great but loading too slow, and no idea why.

    please let me know thanks.

    AM

    Reply
  2. I cant tell you exactly witch is the problem because i don’t know your application architecture. My article is for loading application resources like css, js faster and process it like a regular zend view. A good start is to separate your static content for fast loading.

    Regards.

    Reply

Leave a Comment.