background image

Person 类与 Address 类相似,有一个 setName()和一个 getName()方法,除此之外,
它还有另外两个方法——__construct( )和__call( )。

它的构造器初始化一个 Address 对象,并将该对象保存在一个受保护的$address 属性
中。这样就可以从 Person 类内部访问$address 属性,但不能从外部直接访问它。

在理想的情况下,当我们调用 Address 中的一个方法时,PHP 就会自动运行那个方法。
但这种情况不会发生,因为 Person 并没有扩展 Address。所以,我们必须自己动手编写
代码将调用与适当的方法关联起来。

包装方法是一种选择。例如:

public function setCity($city) {
$this->address->setCity($city);
}

上面的 setCity()方法将数据传递到保存在$address 中的 setCity()方法中。虽然这样很
容易,但也很枯燥,因为我们必须要为每一个方法都写个包装方法。

而使用__call()则可以把这些方法集中起来自动地完成这一过程,如下面例子所示。

publicfunction__call($method,$arguments) {
if(method_exists($this->address,$method)) {
returncall_user_func_array(
array($this->address,$method),$arguments);
}
}

__call()方法会捕捉到任何对未在类中定义的方法的调用。在调用这个方法时,需要传递
两个参数:一个是方法名,另一个是保存着要传递给方法的参数的数组。通过第一个参数
我们可以知道要调用什么方法,所以可以确定是否适合将其分派给$address  

在这里,如果我们检测到这个方法是 Address 类的一个有效的方法,就会传递它。检测
是通过使用 method_ exists()方法并为其提供两个参数—第一个参数是对象,第二个参
数是方法名来进行的。

如果检测结果返回 true,也就是说调用的方法是有效的,那么我们就可以调用它。不幸
的是,我们接下来要面对的是打开$arguments 数组参数的难题。这可是件苦差事。

然而,一个名叫 call_user_func_array()的不常用而且很古怪的函数可以用来解决这个
难题。这个函数可以让我们调用一个用户函数。并且在数组中传递参数。传递给它的第一个
参数是你的函数名,第二个参数是参数数组。

但是,在这里我们想调用的是对象的一个方法而非一个函数。有一种特殊的语法可以用于
这种情况,即不传递函数名,而是代之以传递一个包含两个元素的数组。数组中的第一个
元素是对象,另一个元素是要调用的方法名。

这样,就可以通过 call_user_func_array()调用这个对象的方法了。之后,必须将
call_user_func_array()的结果返回给原始的调用程序,要不然返回的值就会被悄无声