Recent Changes - Search:

Documents

Community

Related Projects

Powered by PmWiki

Filters

NOTE: This is an experimental feature, however, as of HessianPHP 1.0.5 RC2, filters are a core part of the library.

Filters are a powerful mechanism to dynamically add behaviour to an object while promoting separation of concerns. Design is mostly inspired by WebWork's interceptors. WebWork is an excellent web framework for the Java platform.

HessianPHP uses a multipurpose variation of the famous Chain of responsibility design pattern to implement intercepting filters using recursion which, after several tests, proved to be the most performant mechanism for chaining execution in PHP.

Filter objects are chained together in order and each one is executed within the context of a call to a remote method in the proxy. The code inside the filter can be executed before, after or even around the real call.

So far, filters are only available for HessianPHP client objects.

Although this might seem confusing, their use is very simple and straight forward as we will see next.

Creating and using filters

To implement a filter, just create a class that extends InterceptingFilter and declare a function with the following signature: execute(&$context,&$chain). This is the core of the filter. Optionally you can define another method called function init(&$context) that serves for initialization tasks.

The $context argument in both execute() and init() is a reference to the actual HessianProxy object. You can read and even alter values of this object but please don't unset() it or null it out as results are unexpected.

The $chain argument is a reference to the current filter chain and is necessary for "around" and "after" type of filters.

IMPORTANT: If you are creating filters for PHP 4 you *MUST* use the reference operator (&) in the declaration of the arguments of execute() because otherwise you will be working with a copy of the context and the chain and results are unexpected. In PHP 5, since all objects are handled by reference, the & operator is no longer needed.

Example:

class ExampleFilter extends InterceptingFilter{

	function init(&$context){
		echo 'Hi';
	}

	function execute(&$context,&$chain){
		echo 'I am executed before the call'
		$chain->doFilter($context);
		echo 'I am executed after the call'
	}
}

Whatever code is inside the execute function will be executed for every remote call you perform in a client, either HessianClient or HessianProxy. Also, here you implicitly define the type of filter you are creating as we will see in the next section.

Types of filters

Depending in the code inside the execute function, you can create filters of 3 types:

  • Before: Executes before the remote call
  • After: It happens once the remote call is finished
  • Around: Happens before and after the remote call

The way to tell a filter to act as any of these types is the position of the following line:

$chain->doFilter($context);

This line signals the filter mechanism to continue with the next filter in the chain using the context object passed to this function. If this line is not found inside execute, the filter will behave as a "before" filter by default.

Examples:

Before

function execute(&$context,&$chain){
	echo 'Code executed BEFORE the call'
	$chain->doFilter($context);
}

or

function execute(&$context,&$chain){
	echo 'Code executed BEFORE the call'
}

After

function execute(&$context,&$chain){
	$chain->doFilter($context);
	echo 'Code executed AFTER the call'
}

Around

function execute(&$context,&$chain){
	echo 'Code executed AROUND (before) the call'
	$chain->doFilter($context);
	echo 'Code executed AROUND (after) the call'
}

Now that you know how to create filters let's see how to use them in HessianPHP clients.

Registering filters

You can register two types of filter objects in HessianPHP: global filters and by client filters. Ther's an optional name that you can give to any registered filter, if this is omitted, the name of the class will be used instead.

Global filters

These are singletons that are valid for every client object created in the script. These filters live during the whole executing of the script. They are registered using the following call:

Hessian::addFilter( $filterObject, $name );

Where $filter object is a valid filter, for example:

Hessian::addFilter(new LogFilter(), 'my_logger');

Global filters can also be removed by calling Hessian::removeFilter($name_of_filter). Please note that in PHP 4 all class names will be lowercased by default.

Local or by client filters

You can define filters only for some client objects or just one by defining a 'filter' array element in the $options array passed to the constructor of either HessianClient or HessianProxy, like this:

$options = array(
... 
'filters'=>
	array(
		'filter1'=>$filterObjectOne,
		'filter2'=>$filterObjectTwo,
		...
	),
... 
);

The 'filters' array can be an associative array where keys are the names of the filters and values are the objects themselves. If you don't specify a key, the name of the class of the filter will be used by default. For example:

$dump = new StreamDumpFilter();

$options = array(
'out_stream_file'=>'out_php.bin',
'in_stream_file'=>'in_php.bin',
'filters'=>
	array(
		'display'=>new SimpleDisplayFilter(),
		$dump,
	)
);

$client = new HessianProxy($url, $options);

Execution order

Please remember that the order of registration of the filters is very important since they will be chained together at runtime.

When there are both global and by client filters, global filters will be chained first followed by local filters. Since filters are stored in an associative array, global filters can be overriden in clients by defining a filter with the same name of a global filter in the $options array.

Examples

Let's create a simple filter that prints the name of the method being called before is called and after the call is complete. We also want to print the number of methods called so far.

class SimpleDisplayFilter extends InterceptingFilter{
	var $count;

	function SimpleDisplayFilter($count=0){
		$this->count = $count;
	}

	function init(&$context){
		echo 'Initializing url: '.$context->url;
	}

	function execute(&$context,&$chain){
		$this->count++;
		echo 'method number: '.$this->count .'<br>';
		echo 'method: '. $context->callingContext['method'].'()<br>';
		// continue with the chain
		$chain->doFilter($context);
		echo 'Done executing method: '.$context->callingContext['method'].'()';
	}
}

This is an around type of filter which has code before and after passing execution context down in the chain.

Now we will register this filter to be used for every client object and we will make some calls.

Hessian::addFilter(new SimpleDisplayFilter());
$proxy = new HessianClient($url);
$proxy->add(7,7);
$proxy->sub(10,1);

When executing this script we will get this print out:

Initializing url: http://localhost/test.php

method number: 1
method: add()
14
Done executing method: add()

method number: 2
method: sub()
10
Done executing method: sub()

Note that, since this is a global filter, if you create another client and call a method, the next number will be 3, not 1.

Default filters and further examples

In the file Filter.php you will find 4 filters that come with HessianPHP.

  • ProxyFilter is a wrapper filter around the real call on the proxy. This filter is required to correctly operate so please don't modify it.
  • PHPErrorReportingFilter is an around filter that disables PHP notice warning during a remote call. For more details see ErrorHandling.
  • StreamDumpFilter is a filter that writes incoming and outgoing streams to files to perform debugging of the Hessian protocol.
  • LogFilter mantains a log of all remote calls and errors of HessianPHP clients.

For more information on these last two filters see Debugging

A working version of the SimpleDisplayFilter class can be found in the /unit_test folder of the distribution.

TODO: Explain more about this

Edit - History - Print - Recent Changes - Search
Page last modified on January 03, 2006, at 03:49 AM

PmWiki can't process your request

Cannot acquire lockfile

We are sorry for any inconvenience.