LoginSignup
29
29

More than 5 years have passed since last update.

PHPでメソッドを動的に追加する方法

Last updated at Posted at 2012-12-26

JavaScriptではオブジェクトに後からメソッド追加して、オブジェクトを動的に拡張できますよね。

var Object = function() {
    this.foo = "foo";
}

var object = new Object();

object.getFoo = function() {
    return this.foo;
}

console.log(object.getFoo()); // foo

PHP5.4からは Closure::bindTo() を使うとこれと似たようなことができます。

class Object
{
    /**
     * @var \Closure[]
     */
    private $methods = [];

    private $foo = 'foo';

    public function __set(string $name, \Closure $method)
    {
        $this->methods[$name] = $method->bindTo($this, self::class);
    }

    public function __call(string $name, array $arguments)
    {
        if (!array_key_exists($name, $this->methods)) {
            throw new \BadMethodCallException(
                'Call to undefined method ' . __CLASS__ . "::$name()"
            );
        }
        return $this->methods[$name](...$arguments);
    }
}

// オブジェクトを作る
$object = new Object();

// 後からメソッドを追加する
$object->getFoo = function () {
    return $this->foo;
};

// 追加したメソッドを呼び出す
echo $object->getFoo();

ただし、カプセル化を破壊するので、使い所は計画的かつ慎重に判断したほうがいいです。

せっかく trait が使えるので、この動的メソッド追加の処理をトレイト化すると再利用性が高まり、動的メソッド追加を実装するクラスも形式的なコードが少なくなりますね。

trait DynamicMethodDeclaration
{
    /**
     * @var \Closure[]
     */
    private $__dynamicMethods = [];

    public function addMethod(string $name, \Closure $method): void
    {
        $this->__dynamicMethods[$name] = $method->bindTo($this, self::class);
    }

    public function __call(string $name, array $arguments)
    {
        if (!array_key_exists($name, $this->__dynamicMethods)) {
            throw new \BadMethodCallException(
                'Call to undefined method ' . __CLASS__ . "::$name()"
            );
        }
        return $this->__dynamicMethods[$name](...$arguments);
    }
}

class Object
{
    use DynamicMethodDeclaration;

    private $foo = 'foo';
}

// オブジェクトを作る
$object = new Object();

// 後からメソッドを追加する
$object->addMethod('getFoo', function () {
    return $this->foo;
});

// 追加したメソッドを呼び出す
echo $object->getFoo();
29
29
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
29