Delphi for PHP - Integrating the Zend Framework: ZCache - Part 3

Abstract: Jose Leon, Delphi for PHP developer, wrote this article on his blog. In this article, we are going to add caching to the VCL for PHP using the ZCache component we have written on previous articles.

In this article, we are going to add caching to the VCL for PHP using the ZCache component we have written on previous articles. For that, we will need to modify the VCL for PHP source code, and as it is Open Source, there is no problem to do it

This is a perfect example for you to see how easy is to add this kind of things to the library, as we tried to design it as open as possible.

Planning

First, let’s check how to integrate caching on the VCL for PHP. Caching can be useful for many things, but, applied to the VCL for PHP, the first thing that comes to my mind is to allow users, select which components doesn’t get rendered every time the script is called. There are some heavy controls, like DBGrid, that if no data refreshing is required, or not for every request, there is no need to regenerate all code each and every time.

So, to work at a global level, we need to find the common point where all components are rendered, and that is the dumpContents() method, that method is the one a component developer must override to dump the code to the output. But there are some more, in fact, most, if not all methods that dump something to the output start with dump, for example:

dumpHeaderJavascript()

dumpChildrenHeaderCode()

dumpChildrenJavascript()

dumpChildrenFormItems()

dumpChildren()

dumpContents()

A web control can be composed by many things, HTML, Javascript, images, etc, so who is responsible to dump the controls to the browser? The Page component, which usually, iterates through all the controls to call their dump methods at the right moment. On this article we are going only to see how to integrate caching on the show() and dumpContents() methods, you are invited to check the whole code when we finish and submit the code to the VCL for PHP repository.

So, how can we make easy to users to implement caching in their VCL for PHP applications?

Add a property to the Page component to specify which Cache object to use for caching purposes

Add a property named Cached to all Control descendants, to enable/disable caching for that component

Check all methods from Page component that dump something to the output, to implement caching in all of them

Implement caching for all controls in the Control::show() method, which is responsible to call dumpContents()

Well, now we have a plan, so let’s start…

Preparing for expanding

While right now, there is only one cache component on the VCL for PHP library, based on Zend Framework, we must provide a common place holder for third-parties to implement their own cache components, so the first thing we need to do is to create a base class with the common properties and methods for a cache interface.

Let’s create a new unit on the vcl root folder, called cache.inc.php, and write the base class there:

class Cache extends Component

{

/**

* Start caching output, will be called just before start dumping content

* to the output.

*

* This method should use $control and $cachetype to create a unique identifier

* for the cache storage and should start the caching process. If content is not

* cached, should return false to allow the caller know should produce the content.

*

* If the identifier already exists, meaning the content has been already cached,

* then should dump out the cached content and return true.

*

* @param object $control Control to be cached

* @param string $cachetype A prefix to specify what kind of contents are going to be cached

*

* @return boolean True if the content was already cached

*/

function startCache($control, $cachetype)

{

}

/**

* Finish the caching process

*/

function endCache()

{

}

}

Just two methods right now, to start caching and end caching, you can read the comments to know more about the expected behaviour.

Now it’s time to modify the ZCache component, so inherits from Cache and implement those methods:

class ZCache extends Cache

{

function startCache($control, $cachetype)

{

$id=$control->readNamePath().'_'.$cachetype;

$id=str_replace('.','_',$id);

return($this->start($id));

}

function endCache()

{

$this->end();

}

.............................

}

On the startCache() method, we use the control passed to create a unique ID for that cache piece, there is a method on the VCL for that, called readNamePath(), or NamePath property. Also, the type of cache is added to the identifier, so we can cache different control parts, like javascript, html, etc. And finally, call the start() method of the ZCache component, to start caching.

That will create an ID like this: vclapp_Unit139_PearDataGrid1_contents, that is, the Application name, the Unit name, the Component name and the type of cache.

Adding Cache property to the Page

We need to add a Cache property, which is going to be used to specify the ZCache component to use for caching, so let’s add a property:

Notice the fixupProperty method call, you can get more details here, loaded() will also need to be modified.

Preparing the Control class to use the cache

Now that the Page component has a property to allow the user specify the cache component to use, we need to modify the Control base class to use it if available, but only if the Cached property is true. So let’s add that property on the Control base class:

protected $_cached="0";

function getCached() { return $this->_cached; }

function setCached($value) { $this->_cached=$value; }

function defaultCached() { return "0"; }

Default to false, to don’t modify the way applications usually work, and now, we need to modify the show() methods to use cache if available and enabled. Let’s encapsulate those two operations in two methods beginCache and endCache:

function beginCache($type)

{

$result=false;

if (($this->ControlState & csDesigning) != csDesigning)

{

if ($this->_cached==true)

{

if ($this->owner!=null)

{

if ($this->owner->inheritsFrom('Page'))

{

if ($this->owner->Cache!=null)

{

return($this->owner->Cache->startCache($this, $type));

}

}

else if ($this->inheritsFrom('Page'))

{

if ($this->Cache!=null)

{

return($this->Cache->startCache($this, $type));

}

}

}

}

}

return($result);

}

function endCache()

{

if (($this->ControlState & csDesigning) != csDesigning)

{

if ($this->_cached==true)

{

if ($this->owner!=null)

{

if ($this->owner->inheritsFrom('Page'))

{

if ($this->owner->Cache!=null)

{

$this->owner->Cache->endCache();

}

}

else if ($this->inheritsFrom('Page'))

{

if ($this->Cache!=null)

{

$this->Cache->endCache();

}

}

}

}

}

}

All properties are checked to contain the right values, and then, the Cache object in the Cache property is called to start or stop the cache operation.

For the properties we have added, we need to specify the right property editors, that is done on the standard.package.php file:

registerPropertyValues('CustomPage','Cache',array('Cache'));

registerBooleanProperty('Control','Cached');

Testing cache performance

While still there are many things to be polished (caching javascript, for example), we can test right now what we have, to know if caching is worth the effort. So, follow these steps:

Create a new empty Page

Place a ZCache component

Place a Button

Place a PearDataGrid, this is a grid, which is rendered entirely using HTML, and heavy enough to notice caching performance

Assign Cache property of Page to ZCache1

Place a DSN string on the PearDataGrid DSN property, on my case: mysql://root:test@localhost/blog

Place a query on the SQL property, on my case: select id, post_title from wp_posts

Now all the interface is in place, so let’s generate the OnClick handler for the Button, and write this code:

$this->PearDataGrid1->Cached=true;

So when the button is clicked, we are going to tell the grid to be cached.

And finally, to see the performance improvement, add some code at the very top of the page:

$start=microtime(true);

And some code at the very bottom of the page:

$end=microtime(true);

$diff=$end-$start;

$prof=$_SESSION['prof'];

$prof[]=$diff;

$_SESSION['prof']=$prof;

echo "<pre>";

print_r($prof);

echo "</pre>";

So we are measuring how much time takes the page to generate and store it, so everytime the script is called, all the trace is shown.

Now run, and press the button several times, you will get a log like this:

Array

(

[0] => 0.68625521659851

[1] => 0.58160591125488

[2] => 0.39929389953613 <-- First execution with cache!

[3] => 0.41545391082764

[4] => 0.38096499443054

[5] => 0.37384510040283

)

The first time it was executed, no caching was done, and took 0.686 to execute, then, click the button, set Cached to true for the grid, so the content is stored, but still the control is generated, so 0.581 took that time. And after that, all requests are cached, and takes a bit less time to execute. And this is only just caching a simple HTML grid on a simple page, so in a more heavy interface with more controls and content, the saving can be a lot more.

So, with all these three articles, a user can easily implement caching on an application without knowing about the internals of the technology and without having to remember a lot of parameters or stuff, just by placing components and setting properties.