Interceptors are used for aspect oriented programming (AOP). With the given interceptor, method calls can be extended or suppressed. PHP interceptors are implemented using the _call magic method.
Problems
A big problem implementing interceptors in PHP is the fact that method calls of the same class will not be intercepted. To solve the problem, simply call a method with $this->intercepted->myMethod() instead of $this->myMethod(). Methods which are called on intercepted will be intercepted.
Another problem is that the AbstractInterceptor’s methods (e.g. callMethod()) must not be called on the intercepted object from outside.
Implementation
Important: Within the interceptor methods, all calls on the original object must be done on the $object variable (first parameter of the before, around and after method), not on $this. If you use $this, this might omit some interceptors in the interceptor stack.
class AbstractInterceptor {
public $_object;
public $_rootObject;
public function __construct($object) {
$this->_object = $object;
if (is_a($object, "AbstractInterceptor"))
$this->_rootObject = $object->_rootObject;
else
$this->_rootObject = $object;
$object->intercepted = $this;
}
public function callMethod($method, $args){
return call_user_func_array(array($this->_object, $method), $args);
}
public function __isset($name) {
return isset($this->_rootObject->$name);
}
public function __unset($name) {
unset($this->_rootObject->$name);
}
public function __set($name, $value) {
$this->_rootObject->$name = $value;
}
public function __get($name) {
return $this->_rootObject->$name;
}
public function __call($method, $args) {
if ($method[0] == "_")
$method = substr($method, 1);
if (method_exists($this, "before"))
$this->before($this->_rootObject, $method, $args);
if (method_exists($this, "around"))
$value = $this->around($this->_rootObject, $method, $args);
else
$value = $this->callMethod($method, $args);
if (method_exists($this, "after"))
$this->after($this->_rootObject, $method, $args);
return $value;
}
}
class LogInterceptor extends AbstractInterceptor {
function around($object, $method, $args){
print "before $method <br />";
$value = $this->callMethod($method, $args);
print "after $method <br />";
return $value;
}
}
class MyObject {
function myMethod1(){
print "myMethod1 called <br />";
$this->intercepted->myMethod2(); // called with "around" code
$this->myMethod3(); // called directly
}
function myMethod2(){
print "myMethod2 called <br />";
}
function myMethod3(){
print "myMethod3 called <br />";
}
}
$object = new MyObject();
$object = new LogInterceptor($object);
$object->myMethod1();
/* Output:
before myMethod1
myMethod1 called
before myMethod2
myMethod2 called
after myMethod2
myMethod3 called
after myMethod1
*/
Rico Suter
SOFTWARE ENGINEERING
EDIT
Pattern PHP