__call()方法可能是最有用的拦截器方法。当客户端代码要调用类中未定义的方法时,__call()
会被调用。__call()带有两个参数,一个是方法的名称,另一个是传递给要调用方法的所有参数
(数组)。__call()方法返回的任何值都会返回给客户,就好像调用一个真实存在的方法一样
ca11()方法对于实现委托也很有用。委托是指一个对象转发或者委托一个请求给另一个对
象,被委托的一方替原先对象处理请求。这类似于继承,和在子类中调用父类的方法有点相似。
但在继承时,父类与子类的关系是固定的,而使用委托则可以在代码运行时改变使用的对象,这
意味着委托比继承具有更大的灵活性。我们用例子来解释一下。
<?php
class PersonWriter{
function writeName(Person $p){
print $p->getName()."\n";
}
function writeAge(Person $p){
print $p->getAge()."\n";
}
}
<?php
include 'PersonWriter.php';
class Person{
private $writer;
function __construct(PersonWriter $writer){
$this->writer=$writer;
}
function __call($methodname,$args){
if(method_exists($this->writer, $methodname)){
return $this->writer->$methodname($this);
}
}
function getName(){
return "Bob";
}
function getAge(){
return 44;
}
}
$person=new Person(new PersonWriter());
$person->writeName();
代码中Person类接受一个PersonWriter对象作为构造方法的参数,并将它存储在属性变量$writer中。在__call()方法中,我们使用参数$methodname,检查PersonWriter对象中是否存在同名的方法。如果相应的方法存在,我们就委托PersonWriter对象来处理对方法的调用,把当前类(Person)的实例作为参数传递给PersonWriter对象(使用$this伪变量)。因此,如果这样调用Person类:
$person=new Person( new PersonWriter() );
$person->writeName();
__call()方法就会被调用。然后会在PersonWriter对象中查找writeName()方法,并调用之。这样我们就可以不用手动在Person类中调用以下委托方法:
function writeName(){
$this->writer->writeName($this);
}
现在Person类神奇的增加了2个新方法,如果你要委托很多方法的处理,那么这样的自动委托可以为你节省很多时间。但代码也会变得有点不太清晰,不易理解。对于外部世界来说,你提供的是一个动态的接口,没有办法进行反射。因为委托类和被委托类之间的交互可能比较模糊————用__call()方法来调用,而不是显式的继承关系或者参数类型提示。拦截器方法是非常有用的,但是在使用时要慎重考虑,而且最好附上文档,清楚的说明代码细节。