継承無しでクラスを拡張する方法¶
複数のクラスにメソッドを追加ようにするために、次のように拡張したいクラス内でマジックメソッドの __call() メソッドを定義することができます。
class Foo
{
// ...
public function __call($method, $arguments)
{
// 'foo.method_is_not_found' というイベントを作成してください
$event = new HandleUndefinedMethodEvent($this, $method, $arguments);
$this->dispatcher->dispatch($this, 'foo.method_is_not_found', $event);
// イベントを実行するリスナがときは、メソッドが存在していません
if (!$event->isProcessed()) {
throw new \Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
}
// return the listener returned value
return $event->getReturnValue();
}
}
上記のコードでは、特別な HandleUndefinedMethodEvent イベントを使用しているので、作成する必要があります。このクラスは、ジェネリックなクラスで、クラス拡張のこのパターンを使用する際に毎回再利用することができます。必要がある度に、再利用されるジェネリックなクラスです。
use Symfony\Component\EventDispatcher\Event;
class HandleUndefinedMethodEvent extends Event
{
protected $subject;
protected $method;
protected $arguments;
protected $returnValue;
protected $isProcessed = false;
public function __construct($subject, $method, $arguments)
{
$this->subject = $subject;
$this->method = $method;
$this->arguments = $arguments;
}
public function getSubject()
{
return $this->subject;
}
public function getMethod()
{
return $this->method;
}
public function getArguments()
{
return $this->arguments;
}
/**
* 返す値をセットして、通知された他のリスナを止めます
*/
public function setReturnValue($val)
{
$this->returnValue = $val;
$this->isProcessed = true;
$this->stopPropagation();
}
public function getReturnValue($val)
{
return $this->returnValue;
}
public function isProcessed()
{
return $this->isProcessed;
}
}
次に、 foo.method_is_not_found イベントをリッスンして、 bar() メソッドを 追加 します。
class Bar
{
public function onFooMethodIsNotFound(HandleUndefinedMethodEvent $event)
{
// ``bar`` メソッドへの呼び出しのみに応答します
if ('bar' != $event->getMethod()) {
// allow another listener to take care of this unknown method
return;
}
// サブジェクトオブジェクト(foo インスタンス)
$foo = $event->getSubject();
// bar メソッドの引数
$arguments = $event->getArguments();
// 何か実装する
// ...
// 返り値をセットする
$event->setReturnValue($someValue);
}
}
最後に、 Bar のインスタンスを foo.method_is_not_found イベントに登録して bar メソッドを Foo クラスに新しく追加してください。
$bar = new Bar();
$dispatcher->addListener('foo.method_is_not_found', $bar);