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 webpage. As described in the classes 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
<my:tag attr1=“value1″ attr2=“value2″ />
<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:
class my_taglib_tag extends Document {
function my_taglib_tag(){
}
function onParseTime(){
}
function onAfterAppend(){
}
function transform(){
}
}
To get a detailed idea, which functionality should be placed in which method, please have a look at the user specific taglibs 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:
class core_taglib_importdesign extends Document {
function core_taglib_importdesign(){
parent::Document();
} 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:
[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:
// get the cache manager
$cM = &$this->__getServiceObject(‘tools::cache’,‘CacheManager’);
$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:
class cache_taglib_importdesign extends core_taglib_importdesign {
var $__CacheContent = null;
function cache_taglib_importdesign(){
// call the parent’s constructor to fill the known taglib list
parent::core_taglib_importdesign();
}
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();
}
}
function transform(){
// generate the node’s output or return the cached content
if($this->__CacheContent === null){
// get the cache manager
$cM = &$this->__getServiceObject(‘tools::cache’,‘CacheManager’);
$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:
<core:addtaglib namespace=“” prefix=“cache” class=“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 cache manager documentation 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 the package view-based-caching-…-php5.zip into the DOCUMENT_ROOT of your webserver. 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 /apps/config/tools/cache/sites/vbc/DEFAULT_cacheconfig.ini.