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

By: Jose Leon

Abstract: Jose Leon, Delphi for PHP developer, wrote this article on his blog - finishing the component by adding the missing properties and methods, and creating the Zend_Cache component using all properties of the wrapper.

Integrating Zend Framework: ZCache (II)

On the previous article, we saw how to create a wrapper based on a Zend Framework component, so this component can be installed inside Delphi for PHP 2.0 and used visually. We also saw how to add properties and subproperties to that component.

On this article, we will finish the component by adding the missing properties and methods, and creating the Zend_Cache component using all properties of the wrapper.

Adding Backend options

Like Frontends, some backends have specific options, check out the documentation here:

http://framework.zend.com/manual/en/zend.cache.backends.html

So we need to do the same we did with Frontend specific options and add the Persistent properties to create subproperties:

class ZCacheBackendSQLiteOptions extends Persistent

{

protected $_databasepath="";

function getDatabasePath() { return $this->_databasepath; }

function setDatabasePath($value) { $this->_databasepath=$value; }

function defaultDatabasePath() { return ""; }

protected $_vacuumfactor=10;

function getVacuumFactor() { return $this->_vacuumfactor; }

function setVacuumFactor($value) { $this->_vacuumfactor=$value; }

function defaultVacuumFactor() { return 10; }

}

class ZCacheBackendMemcachedOptions extends Persistent

{

protected $_servers=array();

function getServers() { return $this->_servers; }

function setServers($value) { $this->_servers=$value; }

function defaultServers() { return array(); }

protected $_compression="0";

function getCompression() { return $this->_compression; }

function setCompression($value) { $this->_compression=$value; }

function defaultCompression() { return "0"; }

}

After that, we need to add the properties for them:

protected $_backendsqliteoptions=null;

function getBackendSQLiteOptions() { return $this->_backendsqliteoptions; }

function setBackendSQLiteOptions($value) { $this->_backendsqliteoptions=$value; }

function defaultBackendSQLiteOptions() { return null; }

protected $_backendmemcachedoptions=null;

function getBackendMemcachedOptions() { return $this->_backendmemcachedoptions; }

function setBackendMemcachedOptions($value) { $this->_backendmemcachedoptions=$value; }

function defaultBackendMemcachedOptions() { return null; }

And create them in the constructor:

$this->_backendsqliteoptions=new ZCacheBackendSQLiteOptions();

$this->_backendmemcachedoptions=new ZCacheBackendMemcachedOptions();

And, finally, setup some property editors on the package:

registerBooleanProperty("ZCache", "BackendMemcachedOptions.Compression");

registerPropertyEditor("ZCache","BackendMemcachedOptions.Servers","TStringListPropertyEditor","native");

registerPropertyEditor("ZCache","BackendSQLiteOptions.DatabasePath","TFilenamePropertyEditor","native");

And here is the final result:

Executing the app with what we have

If we create a new application, drop a ZCache component in a page and execute it, we will get an error telling us "Cannot serialize a component without an owner", that’s because, the subproperties we have created, inherit from Persistent, but the VCL for PHP streaming system needs to get an owner object, so it’s able to generate a full qualified path when storing the property in the session.

For that, let’s create a base class for all ZCache subproperties, which will return a valid owner, in this case, the ZCache component.

class ZCacheOptions extends Persistent

{

protected $ZCache=null;

function readOwner()

{

return($this->ZCache);

}

function __construct($aowner)

{

parent::__construct();

$this->ZCache=$aowner;

}

}

And now we need to change the base class for all options from Persistent to ZCacheOptions and change the creation of the properties to pass $this in the constructor:

function __construct($aowner = null)

{

//Calls inherited constructor

parent::__construct($aowner);

//Frontend properties

$this->_frontendfunctionoptions= new ZCacheFrontendFunctionOptions($this);

$this->_frontendclassoptions= new ZCacheFrontendClassOptions($this);

$this->_frontendfileoptions= new ZCacheFrontendFileOptions($this);

$this->_frontendpageoptions= new ZCacheFrontendPageOptions($this);

//Backend properties

$this->_backendsqliteoptions=new ZCacheBackendSQLiteOptions($this);

$this->_backendmemcachedoptions=new ZCacheBackendMemcachedOptions($this);

}

So this is it, if we run a simple page with a ZCache component, it won’t show any error.

Creating the Zend_Cache component

Now we have all the information, we need to create the Zend_Cache component, with all the options, depending the Frontend and Backend selected by the user.

We need to add to the component, a member to hold the Zend_Cache instance:

public $zend_cache=null;

It is public, so, in an specific case, the user will be able to use it directly without the wrapper.

Now we have to decide "when" create the Zend_Cache component, and the right moment to do it is in the component initialization, by overriding the preinit() method, which is called by the library when the component has read all its properties and is ready to be used, but before most controls fire their events.
The first thing to do is to translate common Frontend and Backend options to the arrays the Zend_Cache component needs:

$frontendOptions=array();

$backendOptions=array();

//Frontend common properties

$frontendOptions['caching']=$this->_enabled;

$frontendOptions['cache_id_prefix']=$this->_prefix;

$frontendOptions['lifetime']=$this->_lifetime;

$frontendOptions['logging']=$this->_logging;

$frontendOptions['write_control']=$this->_checkwrite;

$frontendOptions['automatic_serialization']=$this->_serialization;

$frontendOptions['automatic_cleaning_factor']=$this->_cleaningfactor;

$frontendOptions['ignore_user_abort']=$this->_ignoreuserabort;

//Backend common properties

$backendOptions['cache_dir']=$this->_cachedir;

$backendOptions['file_locking']=$this->_filelocking;

$backendOptions['read_control']=$this->_checkread;

$backendOptions['read_control_type']=$this->_readcontroltype;

$backendOptions['hashed_directory_level']=$this->_hasheddirectorylevel;

$backendOptions['hashed_directory_umask']=$this->_hasheddirectoryumask;

$backendOptions['file_name_prefix']=$this->_filenameprefix;

$backendOptions['cache_file_umask']=$this->_cachefileumask;

$backendOptions['metadatas_array_max_size']=$this->_metadatasize;

After that, we need to select which Frontend and which Backend will be used depending on the properties, so:

switch ($this->_frontend)

{

case cfOutput:

$frontend='Output';

break;

case cfFunction:

$frontend='Function';

break;

case cfClass:

$frontend='Class';

break;

case cfFile:

$frontend='File';

break;

case cfPage:

$frontend='Page';

break;

}

switch ($this->_backend)

{

case cbFile:

$backend='File';

break;

case cbSQLite:

$backend='Sqlite';

break;

case cbMemcached:

$backend='Memcached';

break;

case cbAPC:

$backend='Apc';

break;

case cbZendPlatform:

$backend='Zend Platform';

break;

}

And finally, create the Zend_Cache component based on those selections:

$this->zend_cache=Zend_Cache::factory($frontend, $backend, $frontendOptions, $backendOptions);

Adding the methods

Now, to allow use the cache, we need to add the methods to allow store and retrieve information, which is simply by calling the Zend_Cache counterparts:

function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)

{

$this->zend_cache->load($id, $doNotTestCacheValidity, $doNotUnserialize);

}

function save($data, $id = null, $tags = array(), $specificLifetime = false)

{

$this->zend_cache->save($data, $id, $tags, $specificLifetime);

}

function start($id, $doNotTestCacheValidity = false)

{

$this->zend_cache->start($id, $doNotTestCacheValidity);

}

function end($tags = array(), $specificLifetime = false)

{

$this->zend_cache->end($tags, $specificLifetime);

}

function remove($id)

{

$this->zend_cache->remove($id);

}

function cleanAll()

{

$this->zend_cache->clean(Zend_Cache::CLEANING_MODE_ALL);

}

function cleanOld()

{

$this->zend_cache->clean(Zend_Cache::CLEANING_MODE_OLD);

}

function cleanMatching($tags)

{

$this->zend_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags);

}

function cleanNotMatching($tags)

{

$this->zend_cache->clean(Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG, $tags);

}

function call($name, $parameters = array(), $tags = array(), $specificLifetime = false)

{

$this->zend_cache->call($name, $parameters, $tags, $specificLifetime);

}

Notice the clean() method has been divided in four, so there is no need to use the Zend_Cache constants.

Testing the component

To make a simple test, let’s drop a ZCache component on a form, leave the properties with the default values, as it will cache output. Now, place a button on the form, and double click it to generate its OnClick event handler. Write this inside the event handler:

function Button1Click($sender, $params)

{

if(!$this->ZCache1->start('mypage'))

{

// output as usual:

echo 'Hello world! ';

echo 'This is cached ('.time().') ';

$this->ZCache1->end(); // the output is saved and sent to the browser

}

echo 'This is never cached ('.time().').';

}

This is a sample from Zend Framework we can use in the same way, but, of course, you don’t have to write so much code and there is no need to remember how to create or initializate the cache, as it is a visual component where all properties are listed, and even valid values for them, are available to be selected from a drop down.

If you click on the button, you will get something like this:

If you click several times, you will see the first number it doesn’t change, that means that the output has been cached, so it’s dumped out directly from the cache instead to be executed, not like the second number that is executed every time.

Well, in this second article, we have finished the component and it’s ready to be used, we have not documented how to use the subproperties when creating the Zend_Cache component, because that will make a very long article.

In any case, in the next article, we will see how to integrate it within VCL for PHP, so it’s easy for user to select which components or pages are cached.

Hope you like it!


Published on: 11/18/2008 12:00:00 AM

Server Response from: ETNASC02

Copyright© 1994 - 2013 Embarcadero Technologies, Inc. All rights reserved.