How to implement view based caching

1. Introduction

Within a discussion in the developers-guide.net forum, the idea of view based caching was born. Triggered by Alberto, we began to talk about performance optimization and various caching methods. The conclusion was, that caching of pieces of HTML is an effective way to gain performance.

This can be done by the cURL solution mentioned by Alberto or by view based caching. The benefit of view based caching method is, that the application itself must not be touched or adapted to the caching strategy. Within this article, I will talk about the idea of this technique and the implementation with the resources of the Adventure PHP Framework.


2. What about views?

First of all, let me say a few words about the notion view. Spoken in APF terminology, a view is nothing else, than a template file included within another - maybe a layout template of your web page. As described in the Page controller section, the presentation layer implementation of the APF features a generic page controller component, that analyzes APF style template files and generates a DOM tree out of the known tags.

Each tag consists of it's tag definition - something like
APF template
<my:tag attr1="value1" attr2="value2" />
and a class including the functionality of the tag. Each taglib must implemente the following methods, that are executed in the order presented in the code box:
PHP code
class MyTag extends Document { public function onParseTime(){ } public function onAfterAppend(){ } public function transform(){ } }

To get a detailed idea, which functionality should be placed in which method, please have a look at the Implementation of tags tutorial.

So how can this fact be used to implement view based caching? Thanks to the generic DOM definition, the developer is able to implement own taglibs to enhance the core functionality or to satisfy the customer's requirements. This means, that the existing template including mechanisms can be used as a basis for view based caching.

3. Implementation

3.1. Let's get started

As described on the Standard taglibs page, the <core:importdesign /> tag imports a template specified by it's namespace and file name into the current DOM node. As I have mentioned above, this tag can be used to specify dedicated views within a layout template containg the header, footer or navigation functionality. Due to the fact, that we want to cache the content of special views, we can use this taglib as a basis.

To be sure about that, let us have a look at the taglib implementation:
PHP code
class ImportTemplateTag extends Document { public function __construct(){ parent::__construct(); } public function onParseTime(){ $namespace = trim($this->attributes['namespace']); $template = trim($this->attributes['template']); if(isset($this->attributes['context'])){ $this->context = trim($this->attributes['context']); } if(isset($this->attributes['incparam'])){ $incParam = $this->attributes['incparam']; } else{ $incParam = 'pagepart'; } ... $this->loadContentFromFile($namespace, $template); $this->extractDocumentController(); $this->extractTagLibTags(); } }
The main functionality is to evaluate the tag's attributes injected on parse time, to load and parse the content using the extractDocumentController() and extractTagLibTags() methods. The only thing, that is missing, is the caching part!


3.2. The cache manager

For caching purposes, the framework contains a flexible caching component with various backends. In this case, we simply use the text cache mechanism described in the text cache provider section in the documentation. In order to use it we have to provide a configuration section within the cache configuration file as described in the docs. The section might look like this:
APF configuration
[view_based_cache] Cache.Provider.Namespace = "tools::cache::provider" Cache.Provider.Class = "TextCacheProvider" Cache.Active = "true" Cache.BaseFolder = "/path/to/my/cache/base/folder" Cache.Namespace = "view::one"
To read and write the cache respectivly, we can use the following code fragment:
PHP code
// get the cache manager $cMF = &$this->getServiceObject('tools::cache','CacheManagerFabric'); $cM = &$cMF->getCacheManager('view_based_cache'); // calculate cache key $cacheKey = /* ... */; // read the cache $cacheContent = $cM->getFromCache($cacheKey); // write to the cache if($cacheContent === null){ $cacheContent = /* generate content */; $cM->writeToCache($cacheKey,$cacheContent); }

3.3. The final assembly

To get things working, let's put the pieces together:
PHP code
class CachedImportTemplateTag extends ImportTemplateTag { private $cacheContent = null; public function __construct(){ // call the parent's constructor to fill the known taglib list parent::__construct(); } public function onParseTime(){ // get the cache manager $cMF = &$this->getServiceObject('tools::cache','CacheManagerFabric'); $cM = &$cMF->getCacheManager('view_based_cache'); // calculate the cache key $cacheKey = md5( $this->getAttribute('namespace'). $this->getAttribute('template'). get_class($this->parentObject) ); // try to read from the cache $this->cacheContent = $cM->getFromCache($cacheKey); // check if the document was cached before. If not // execute the parent's onParseTime() if($this->cacheContent === null){ parent::onParseTime(); } } public function transform(){ // generate the node's output or return the cached content if($this->cacheContent === null){ // get the cache manager $cMF = &$this->getServiceObject('tools::cache','CacheManagerFabric'); $cM = &$cMF->getCacheManager('view_based_cache'); // calculate the cache key $cacheKey = md5( $this->getAttribute('namespace'). $this->getAttribute('template'). get_class($this->parentObject) ); // generate output and cache it $output = parent::transform(); $cM->writeToCache($cacheKey,$output); // return the tag's output return $output; } else{ return $this->cacheContent; } } }
As you can see, the onParseTime() and transform() methods are enhanced with the cache handling. To store the cache content and to be able to decide, if cached content is available, the private member variable cacheContent was introduced. Please note, that the code duplication within the two methods is just there to illustrate the function flow.


4. Usage

The usage of the tag is not different to the usage of the <core:importdesign /> tag. The only difference is, that we have to provide a configuration section for the cache manager:
APF template
<core:addtaglib namespace="..." class="CachedImportTemplateTag" prefix="cache" name="importdesign" /> <cache:importdesign namespace="my::namespace" template="my_template" />

5. Conclusion and outlook

Thanks to the generic page controller implementation of the APF it is quite easy to implement a view based cache concept using taglibs. To have a robust and reusable taglib, the class printed above should have one or two more attributes, specifying the cache configuration section and perhaps the cache key. Doing so, the taglib is surely reusable within other projects or within several parts of your application.

Concerning the cache control, you have the choice to either clear the cache manually or use a cache provider, that can handle cache file life time. If you have the need, to add automatical refreshment, have a look at the enhancement chapter on the CacheManager page.


6. Download

To try this out, I have created a sample implementation for PHP 5 including the enhancements written about in chapter 5. If you want to run the sample code, just download and extract package apf-vbc-example-* from the Downloads page into the DOCUMENT_ROOT of your web server. Please be sure, that the user running your HTTP server has write permissions to the current folder. If you like to use another folder to store the cache, adapt the file /APF/config/tools/cache/sites/vbc/DEFAULT_cacheconfig.ini.

Comments

Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.