For a long time I’ve been pondering how to modernize the concept of “hooks”, as seen in Drupal.
The hook system is basically a global event manager - although I don’t have a list of good things to say about the Drupal implementation in particular, the pattern is very interesting, and it’s what makes Drupal so extensible.
For a while I’ve been wondering how to implement this pattern in a clean, modern way in PHP, and tonight I came up with something interesting.
<?php
interface Interceptable
{
public function __dispatch($method, $params);
}
abstract class Interceptor
{
public static function dispatch(Interceptable $object, $method, $params)
{
echo "Intercepted a call to $method... ";
return call_user_func_array(array($object, '__dispatch'), array($method, $params));
}
}
class Test implements Interceptable
{
public function __call($method, $params)
{
return Interceptor::dispatch($this, $method, $params);
}
public function __dispatch($method, $params)
{
return call_user_func_array(array($this,$method), $params);
}
protected function callMe()
{
echo "Test::callMe() was invoked!";
}
}
$test = new Test;
$test->callMe();
In this example, the abstract Interceptor class provides the global "hook" that intercepts method calls, in the form of a dispatch() method.
Any class that wishes to implement interceptable method calls, must implement the Interceptable interface, which provides a means for the Interceptor to invoke a private or protected method inside an interceptable class.
The Test class in this example must implement two methods: the __call() method, from which it delegates control up to Interceptor::dispatch() - and the __dispatch() method, which enables the Interceptor to invoke the protected callMe() method.
Clearly a lot of things are still missing here, but basically this mechanism lets me intercept method calls, which would enable me to to pre-process parameters, and post-process the return value, for any method call.
This technique could enable basic aspect-oriented programming, leveraging cross-cutting concerns, etc.
Clearly this is a hack - but it’s a lot cleaner than code generation, eh?