PHP|基于拦截器的委托方法的实践

362 查看

PHP提供了内置的拦截器方法,它可以拦截发送到未定义方法和属性的消息。也被称为重载(overloading)。

拦截器方法

    __get($property) // 访问未定义的属性时被调用
    __set($property, $value) // 给未定义的属性赋值时被调用
    __isset($property) // 给未定义的属性调用isset()时被调用
    __unset($property) // 给未定义的属性调用unset()时被调用
    __call($method, $arg_array) //调用未定义的方法时被调用

PHP经常使用静态术语的表达方式(即::符号)来讨论类方法与属性,即使改方法和属性并非静态。当提及Person::$name属性时,要注意name属性不一定是静态属性,很可能需要通过对象来访问。

当创建Person对象并尝试设置一个名为Person::$name的属性时,因为这个类没有定义$name属性,所以__set()方法被调用。

__call方法对于实现委托也很有用。委托是指一个对象转发或者委托一个请求给另一个对象,被委托的一方帮忙处理请求。

举个例子

class PersonWriter
{
    public function writeName(Person $p) {
        print $p->getName() . PHP_EOL;
    }
    
    public function writeAge(Person $p) {
        print $p->getAge() . PHP_EOL;
    } 
}

class Person
{
    private $writer;
    
    public function __construct(PersonWriter $writer) {
        $this->writer = $writer;
    }
    
    public function __call($methodname, $args) {
        if (method_exists($this->writer, $methodname)) {
            return $this->writer->methodname($this);
        }
    }
    
    public function getName() {
        return "Bob";
    }
    
    public function getAge() {
        return 44;
    }
}

调用

$person = new Person(new PersonWriter());
$person->writeName();

此处提供了一个动态的接口,来让Person对接PersonWriter,也是提供一种思路。

更进一步

如果在拦截器里使用call_user_func()方法会更好

function __call($method, $args) {
    if (method_exists($this->obj, $method)) {
        return call_user_func_array([$this->obk, $method], $args);
    }
}