. namespace hoplite\base; /*! A WeakInterface is a class that implements some interface and then can bind a subset of those methods to some other object. This allows a delegate pattern to be implemented more easily in that the delegate class need not implement every method. To use this class, define an interface as you normally would. When making a call to an object that implements the interface, create a WeakInterface object and instead make the calls on that. */ class WeakInterface { /*! @var array Map of method imps hashes, keyed by the method name. */ private $imps = array(); /*! @var object The object to which this interface is bound. */ private $object = NULL; /*! @var ReflectionClass The reflector of #object. */ private $object_reflector = NULL; /*! @var bool Whether or not a NULL object will throw when called. */ private $null_allowed = TRUE; /*! Creates a weak interface with the name of an actual interface. */ public function __construct($interface) { $reflector = new \ReflectionClass($interface); $methods = $reflector->GetMethods(); foreach ($methods as $method) { $name = $method->name; $imp = array( 'method' => $method, 'required' => strpos($method->GetDocComment(), '@required') !== FALSE, ); $this->imps[$name] = $imp; } } /*! Gets the object to which this interface is bound. */ public function get() { return $this->object; } /*! Sets whether NULL is allowed. */ public function set_null_allowed($flag) { $this->null_allowed = $flag; } /*! Binds an object to the interface. */ public function Bind($object) { $this->_CheckObject($object); $this->object = $object; $this->object_reflector = new \ReflectionClass($this->object); } /*! Magic method that performs the actual method invocation. */ public function __call($meth, $args) { if (!$this->object) { if (!$this->null_allowed) throw new WeakInterfaceException('NULL object when calling ' . $meth); else return; } try { $imp = $this->object_reflector->GetMethod($meth); } catch (\ReflectionException $e) { if ($this->imps[$meth]['required']) throw $e; return; } return $imp->InvokeArgs($this->object, $args); } /*! Ensures that the bound object properly implements the interface. */ private function _CheckObject($object) { $reflector = new \ReflectionClass($object); $methods = $reflector->GetMethods(); $by_name = array(); foreach ($methods as $method) { $by_name[$method->name] = $method; } foreach ($this->imps as $name => $imp) { if ($imp['required'] && !isset($by_name[$name])) throw new WeakInterfaceException($reflector->name . ' does not implement required interface method ' . $name); if (isset($by_name[$name]) && $imp['method']->GetParameters() != $by_name[$name]->GetParameters()) throw new WeakInterfaceException($reflector->name . '::' . $name . ' does not match interface definition'); } } } class WeakInterfaceException extends \Exception {}