匿名函数不是严格意义上的面向对象的特性,但它非常有用,因为可能会在使用回调面向对象的应用中遇到她。
请看下面两个类:
1 <?php 2 class Product 3 { 4 public $name; 5 public $price; 6 7 function __construct($name, $price) 8 { 9 $this->name = $name; 10 $this->price= $price; 11 } 12 } 13 14 class ProcessSale 15 { 16 private $callbacks; 17 18 function register_callback($callback) 19 { 20 if(!is_callable($callback)) //判断是否可以调用 21 { 22 throw new Exception('callback not callable'); 23 } 24 25 $this->callbacks[] = $callback; 26 } 27 28 function process($product) 29 { 30 print "{$product->name}: processing\n"; 31 32 foreach($this->callbacks as $callback) 33 { 34 call_user_func($callback, $product); 35 } 36 } 37 } 38 39 ?>
这段代码的目的是运行各种回调。Product只存储$name和$price。Processsale由2个方法构成,registercallback()接受一个不提示的标量,测试该标量并将其添加到回调数组中。实现测试功能的内置函数is_callable()函数,该函数确保传递进来的值被call_user_func()或array_walk()等函数调用。
process()方法接受一个product对象,输出与该对象有关的一条信息。然后遍历$callbacks数组属性。
回调的好处:利用回调可以再运行时将与该组件的核心任务没有直接关系的功能插入到组件中。有了组件回调,你就赋予了其他人在你不知道的上下文扩展你的代码的权利。
现在假如用户想要创建一条销售记录。如果该用户可以直接访问该类,则可以在process()方法中添加记录逻辑,但有时这种做法不好。如果他不是该类的维护者,那么她对该类的修改会在下次更新时会覆盖。即使他是该组件的维护者,想process()方法添加那么多的附加任务也是本末倒置,无法体现该方法的核心功能,这可能导致该方法跨项目的可能性降低。
还好,我们创建了process回调。
1 <?php 2 class Product 3 { 4 public $name; 5 public $price; 6 7 function __construct($name, $price) 8 { 9 $this->name = $name; 10 $this->price= $price; 11 } 12 } 13 14 class ProcessSale 15 { 16 private $callbacks; 17 18 function register_callback($callback) 19 { 20 if(!is_callable($callback)) //判断是否可以调用 21 { 22 throw new Exception('callback not callable'); 23 } 24 25 $this->callbacks[] = $callback; 26 } 27 28 function process($product) 29 { 30 print "{$product->name}: processing\n"; 31 32 foreach($this->callbacks as $callback) 33 { 34 call_user_func($callback, $product); 35 } 36 } 37 } 38 39 $logger = create_function('$product', 'print " logging ({$product->name})\n";'); 40 $p1 = new ProcessSale(); 41 $p1->register_callback($logger); 42 43 $p1->process(new Product("shoes", 8)); 44 print "\n"; 45 46 $p1->process(new Product("coffee", 9)); 47 ?>
结果自行运行。
PHP 5.3及以后的版本中提供了更好的方法来实现匿名函数。
1 $logger = function($product) 2 { 3 print " logging ({$product->name})\n"; 4 }; 5 6 $p1 = new ProcessSale(); 7 $p1->register_callback($logger); 8 9 $p1->process(new Product("shoes", 8)); 10 print "\n"; 11 12 $p1->process(new Product("coffee", 9));
该方法是以内联的方式使用function关键字,并且没有函数名。注意,因为这是一条内联语句,所以在代码块的末尾需要使用分号。
回调并不一定要匿名的,你可以使用函数名(甚至是对象引用和方法)作为回调。