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);
}
}