I don't really like the FilterChain and addFilter($filter)-command writing (just my personal feelings about it ;)). I have come up with the code below. I tried to follow the Decorator-pattern followed very strictly.
The abstract Component is the class Controller:
PHP Code:
// abstract Component in the Decorator-pattern
class Controller
{
var $request;
var $response;
function Controller(&$request, &$response)
{
$this->request =& $request;
$this->response =& $response;
}
function process() {}
}
The abstract Decorator is the class Filter:
PHP Code:
// abstract Decorator in the Decorator-pattern
class Filter extends Controller
{
var $filter;
function Filter(&$filter)
{
$this->filter =& $filter;
$this->request =& $this->filter->request;
$this->response =& $this->filter->response;
}
function process()
{
$this->postProcess();
$this->filter->process();
$this->preProcess();
}
function postProcess()
{
}
function preProcess()
{
}
}
Then I have the class FrontController which is a concrete Component:
PHP Code:
// concrete Component in the Decorator-pattern
class FrontController extends Controller
{
function FrontController(&$request, &$response)
{
parent::Controller($request, $response);
}
function process()
{
$command =& $this->getCommand($this->request, $this->response);
$command->process();
}
// can also be used whithout initiating a FrontController. If not needed then change
function &getCommand(&$request, &$response)
{
$commandClassName =& $this->getCommandClass($request);
return new $commandClassName($request, $response);
}
// can also be used whithout initiating a FrontController. If not needed then change
function getCommandClass(&$request)
{
$commandClassName = ($request->getParameter('command'))
? $request->getParameter('command').'Command'
: 'NoCommand';
if (file_exists(PATH_CONTROLLER.$commandClassName.'.php'))
{
include_once(PATH_CONTROLLER.$commandClassName.'.php');
return $commandClassName;
}
else
{
trigger_error($commandClassName.'.php doesn\'t exist!', E_USER_ERROR);
}
}
}
The FrontController calls a command (a PageController) which in it self is also a concrete Component. A example is the class NoCommand (but every command(\PageController) is build like this:
PHP Code:
// concrete Component in the Decorator-pattern
class NoCommand extends Controller
{
function NoCommand(&$request, &$response)
{
parent::Controller($request, $response);
}
function process()
{
echo "<p>NoCommand:process</p>\n";
}
}
A Filter (a Concrecte Decorator) is constructed like this:
PHP Code:
// concrete Decorator in the Decorator-pattern
class AuthFilter extends Filter
{
function AuthFilter (&$filter)
{
parent::Filter($filter);
}
function process()
{
parent::process();
}
function postProcess()
{
echo "<p>AuthFilter:post</p>\n";
}
function preProcess()
{
echo "<p>AuthFilter:pre</p>\n";
}
}
or like this:
PHP Code:
// concrete Decorator in the Decorator-pattern
class TimingFilter extends Filter
{
var $start_time;
function TimingFilter(&$filter)
{
parent::Filter($filter);
}
function process()
{
parent::process();
}
function postProcess()
{
$this->start_time = $this->getMicroTime();
echo "<p>TimingFilter:post ".($this->getMicroTime()- $this->start_time)."sec</p>\n";
}
function preProcess()
{
echo "<p>TimingFilter:pre ".($this->getMicroTime()- $this->start_time)."sec</p>\n";
}
function getMicroTime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
}
I would use it like this:
PHP Code:
$request =& new HttpRequest;
$response =& new HttpResponse;
$application =& new FrontController($request, $response);
$application->process();
When I need a Filter I would do this (and if need more I would nest more):
PHP Code:
$request =& new HttpRequest;
$response =& new HttpResponse;
$application =& new TimingFilter(new AuthFilter(new FrontController($request, $response)));
$application->process();
or change the getCommand-method in the FrontController, like:
PHP Code:
function &getCommand(&$request, &$response)
{
$commandClassName =& new TimingFilter(new AuthFilter($this->getCommandClass($request)));
return new $commandClassName($request, $response);
}
If I don't need a FrontController and want to use a Command(\PageController) directly, I can use it the same way:
PHP Code:
$request =& new HttpRequest;
$response =& new HttpResponse;
$application =& new TimingFilter(new AuthFilter(new NoCommand($request, $response)));
$application->process();
Do you think this to be a good way? Are I making some big errors? One thing is that if you want to put Filters dynamicly based on a configuration, then addFilter($filter) is easier, like:
PHP Code:
foreach($filters as $filter)
{
$fc->addFilter($filter);
}
-Rémy
Bookmarks