Articles » How to implement view based caching
How to implement view based caching
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.
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
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 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.
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 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!
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-Konfiguration
[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);
}
To get things working, let's put the pieces together:
PHP-Code
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
$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.
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="" prefix="cache" class="importdesign" />
<cache:importdesign namespace="my::namespace" template="my_template" />
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.
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.
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.