定义对象的字符串值
PHP5中引入的另一个受java启发的功能是__toString()方法。通过实现自己的__toString()方法,你可以控制输出字符串的格式。__toString()方法应当返回一个字符串值。当把对象传递给print或echo时,会自动调用这个方法,并用方法的返回值来替代默认的输出内容。
<?php
class Person
{
public function getName()
{
return "Bob";
}
public function getAge()
{
return 44;
}
function __toString()
{
$desc = $this->getName();
$desc .= "(age" . $this->getAge() . ")";
return $desc;
}
}
$person = new Person();
print $person;//输出Bob (age 44)
对于日志和错误报告,__toString()方法非常有用。__toString()方法也可用于设计专门用来传递信息的类,比如Exception类可以把关于异常数据总结信息写到__toString()方法中。
回调、匿名函数和闭包
首先,来看两个类:
class Product
{
public $name;
public $price;
function __construct($name,$price)
{
$this->name=$name;
$this->price=$price;
}
}
class ProcessSale
{
private $callbacks;
function registerCallback($callback)
{
if (!is_callable($callback)) {
throw new Exception("callback not callback");
}
$this->callbacks[] = $callback;
}
function sale($product)
{
print "($product->name}:processing \n";
foreach ($this->callbacks as $callback) {
call_user_func($callback, $product);
}
}
}
$logger = create_function('$product', 'print " logging ({$product->name})\n";');
$processor = new ProcessSale();
$processor->registerCallback($logger);
$processor->sale(new Product("shoes", 6));
print "\n";
$processor->sale(new Product("coffee", 6));
registerCallback()接受一个不提示的标量,测试该标量并将其添加到回调数组中。实现测试功能的是内置的is_callable()函数,该函数能确保传递进来的值能被call_user_func()或array_walk()等函数调用。
sale()方法接受一个Product对象,输出与该对象有关的一条信息,然后遍历$callback数组属性。该方法会把每个元素传递给调用该代码的call_user_func(),也就是将产品的引用传递给它。
利用回调,你可以在运行时将与组件的核心任务没有直接关系的功能插入到组件中。有了组件回调,你就赋予了其他人在你不知道的上下文中扩展你的代码的权利。
我们使用create_function()创建回调,结果通常被称为匿名函数。你也可以简单地在一条语句中声明并分配函数:
$logger = function ($product) {
print " logging ({$product->name})\n";
};
注意,因为这是一条内联语句,所以在代码块的末尾需要使用分号。
当然,回调并不一定是匿名。您可以使用函数名(甚至是对象引用和方法)作为回调:
class Mailer
{
function doMail($product)
{
print " mailing({$product->name})\n";
}
}
$processor = new ProcessSale();
$processor->registerCallback(array(new Mailer(), "doMail"));
$processor->sale(new Product("shoes", 6));
print "\n";
$processor->sale(new Product("coffee", 6));
调用registerCallback()时,传递给它一个数组。数组的第一个元素是Mailer对象,第二个元素是字符串(该字符串与想要调用的方法名称相匹配)。is_callable()非常智能,能够测试这类数组。数组形式的有效回调应该以对象作为其第一个元素,以方法名作为其第二个元素。
当然,你也可以让方法返回匿名函数:
class Totalizer
{
static function warnAmount()
{
return function ($product) {
if ($product->price > 5) {
print " reached high price:{$product->price}\n";
}
};
}
}
$processor = new ProcessSale();
$processor->registerCallback(Totalizer::warnAmount());
除了使用warnAmount()作为匿名函数的工厂方法很方便之外,这里面有什么有趣的内容了。但除了生成匿名函数之外,利用该结构还可以做更多的事儿,比如利用闭包。这些新风格的匿名函数可以引用在其父作用域中声明的变量。
class Totalizer
{
static function warnAmount($amt)
{
$count = 0;
return function ($product) use ($amt, &$count) {
$count += $product->price;
print " count:$count\n";
if ($count > $amt) {
print " reached high price:{$count}\n";
}
};
}
}
$processor = new ProcessSale();
$processor->registerCallback(Totalizer::warnAmount(8));
//...