動的Proxy


オブジェクト生成時のいろいろな指定はとりあえず無視しとく。

function p($s)
{
    echo $s . "\n";
}

class Object
{
    function __get($name)
    {
        if (!isset($this->{$name})) {
            $this->{$name} = new $name();
        }
        return $this->{$name};
    }
}

class foo extends Object
{
    //var $bar; 指定してしまうと__getが使えないのがネックかも
    
    function __construct()
    {
        p('foo::construct');
    }
    
    function hoge($s)
    {
        p('foo::hoge');
        
        return $this->bar->hoge($s.'foo');
    }
}

class bar extends Object
{
    //var $baz;
    
    function __construct()
    {
        p('bar::construct');
    }
    
    function hoge($s)
    {
        p('bar::uni');
        return $this->baz->hoge($s.'|bar');
    }
}

class baz
{
    function __construct()
    {
        p('baz::construct');
    }

    function hoge($s)
    {
        p('baz::uni');
        return $s.'|baz';
    }
}

メリット

  • シンプル
  • 余計なオブジェクト(プロキシクラス含む)を生成しない

デメリット

  • 親クラスを継承する必要がある
  • プロパティに指定を書けない
function p($s)
{
    echo "$s\n";
}

class Container
{
    static function create($class)
    {
        $result = new $class();
        foreach ($result as $p => $v) {
            $result->{$p} = new Proxy($p);
        }
        return $result;
    }
}

class foo
{
    var $bar;//Proxy
    
    function __construct()
    {
        p('foo::construct');
    }
    
    function hoge($s)
    {
        p('foo::hoge');

        return $this->bar->hoge($s.'|foo');
    }
}

class bar
{
    var $baz;
    
    function __construct()
    {
        p('bar::construct');
    }
    
    function hoge($s)
    {
        p('bar::hoge');
        return $this->baz->hoge($s.'|bar');
    }
}

class baz
{
    function __construct()
    {
        p('baz::construct');
    }

    function hoge($s)
    {
        p('baz::hoge');
        return $s.'|baz';
    }
}

class Proxy
{
    private $ref;
    private $instance;
    
    function __construct($class)
    {
        $this->ref = new ReflectionClass($class);
    }
    
    function __get($name)
    {
        if (!isset($this->instance)) {
            $this->__instantiate();
        }
        return $this->instance->{$name};
    }
    
    function __set($name, $value)
    {
        if (!isset($this->instance)) {
            $this->__instantiate();
        }
        $this->instance->{$name} = $value;
    }
    
    function __call($name, $args)
    {
        if (!$this->ref->hasMethod($name)) {
            // error
            echo "error\n";
        }
        if (!isset($this->instance)) {
            $this->__instantiate();
        }
        return $this->ref->getMethod($name)->invokeArgs($this->instance, $args);
    }

    function __instantiate()
    {
        $this->instance = Container::create($this->ref->getName());
    }
}

$foo = Container::create('foo');
p ($foo->hoge('ccc') );