A tutorial on php + xcache for serving webpages directly from server RAM

A typical PHP application and most of the frameworks uses “The Loop” method to code the website. Whenever request comes to website, It’s typically sent to index page to handle every parameters.

// The world's simplest index page, also called The Loop
get_header();
if (have_action()) :
	if(found_action()):
		include(action);
	else:
		header("404");
else :
	  include("homepage");
endif;
get_footer();

Above code describes typical PHP application routing for template based websites. This style of coding can be converted into enterprise PHP framework with use of design patterns (i.e interfaces like pear, Template engine like smarty, use of MVC).

Xcache and Lighttpd

Xcache optimize PHP performance by keeping most frequently used code into server RAM. i.e. Once code is cached, repeated requests are directly served without recompiling. There are several PHP accelerators are available that goes well on different environments i.e. APC, memcache, xcache, static cache etc.

Accelerator on Lighttpd No of Requests Concurrent connections Serving Time
static File Cache 20000 5 43 seconds
memcache 20000 5 19 seconds
xcache 20000 5 13 seconds

I made some ab (apache benchmark) test on lighttpd with xcache, memcache and static file cache & I have to conclude that Lighttpd and Xcache combination gives best php performance.

Best Pairs of Accelerator + Web Server

  • APC + Apache = Best performance
  • Memcached + Nginx = Best performance
  • Xcache + Lighttpd = Best performance

The Loop Framework with Xcache

We can use xcache variables to store entire web page and serve it from RAM.

Xcache Functions

  • xcache_isset($key) – Checks for $key exist in xcache
  • xcache_set(“key”,”value”,$timeout) – Sets variable in xcache for $timeout seconds
  • xcache_get(“key”) – Gets variable from xcache
  • xcache_unset(“key”) – Removes variable from xcache

Using xcache with php to optimize website.

// Init Caching
include("xcache.php"); // Including Xcache Library... (find code below)
define('HTML_CACHE',true); // set this to false when you need to turn off xcache feature
$cacheConfig['cachepage'] = true; // for mobile useragent you may turn it off for fetching and displaying ads.
init_caching();

get_header();
if (have_action()) :
	if(found_action()):
		include(action);
	else:
		header("404");
else :
	  include("homepage");
endif;
get_footer();  

// Finish Caching
finish_caching();

xcache.php

// code for xcache.php
global $cacheConfig;
$cacheConfig = Array();
$cacheConfig['timeout'] = 10800; // 3 hours
$cacheConfig['page'] = md5("http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']);
$cacheConfig['savepage'] = false;
$cacheConfig['cachepage'] = false;

function cacheEnabled() {
	if (!defined('HTML_CACHE') || (defined('HTML_CACHE') && HTML_CACHE != true)) {
		return false;
	}else{
		return true;
	}
}

function init_caching() {
	if (!cacheEnabled()) {
		return;
	}
	global $cacheConfig;
	if ($cacheConfig['cachepage'] == true) {
	startHTMLCache();
	}
}

function finish_caching() {
	if (!cacheEnabled() ){
		return;
	}
	global $cacheConfig;
	if ($cacheConfig['savepage'] == true) {
	endHTMLCache();
	}
}

function endHTMLCache() {
		global $cacheConfig;
		$content = ob_get_contents();
		ob_end_clean();
		xcache_set($cacheConfig['page'],$content,$cacheConfig['timeout']);
		echo $content;
		exit;
}

function startHTMLCache() {
		global $cacheConfig;
		if (xcache_isset($cacheConfig['page']))	{
			echo xcache_get($cacheConfig['page']);
			exit;
		}
  		else {
    	  $cacheConfig['savepage'] = true;
		   ob_start();
  	 	}
}