[关闭]
@a5635268 2016-05-09T15:52:51.000000Z 字数 7243 阅读 1430

PHP的匿名函数和闭包

PHP


匿名函数

  1. // Example1
  2. $func = function( $param ) {
  3. echo $param;
  4. };
  5. $func( 'some string' );//输出:some string
  6. // Example2
  7. function callFunc( $func ) {
  8. $func( 'some string' );
  9. }
  10. $printStrFunc = function( $str ) {
  11. echo $str;
  12. };
  13. callFunc( $printStrFunc );
  14. //可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉
  15. callFunc( function( $str ) {
  16. echo $str;
  17. } );

闭包

PHP在默认情况下,匿名函数内不能调用所在代码块的上下文变量,而需要通过使用use关键字。

  1. //1. 通过闭包获取当前函数环境外的变量值副本。
  2. function getMoney() {
  3. $rmb = 1;
  4. $dollar = 6;
  5. $func = function() use ( $rmb ) {
  6. echo $rmb; //1
  7. echo $dollar; //报错,找不到dorllar变量
  8. };
  9. $func();
  10. }
  11. getMoney();
  12. //2. 之所以称为副本,是因为通过闭包传值到匿名函数内的变量,值也是不能改变。
  13. function getMoney() {
  14. $rmb = 1;
  15. $func = function() use ( $rmb ) {
  16. $rmb += 2;
  17. echo $rmb; // 3
  18. };
  19. $func();
  20. echo $rmb; // 还是1没有改变;
  21. }
  22. getMoney();
  23. //3. 如果要改变外部变量的值,还是得通过传址的方法
  24. function getMoney() {
  25. $rmb = 1;
  26. $func = function() use ( &$rmb ) {
  27. $rmb += 2;
  28. echo $rmb; // 3
  29. };
  30. $func();
  31. echo $rmb; // 3;
  32. }
  33. getMoney();
  34. //4.
  35. function getMoneyFunc() {
  36. $rmb = 1;
  37. $func = function() use(&$rmb){
  38. echo $rmb;
  39. //把$rmb的值加1
  40. $rmb++;
  41. };
  42. return $func; // 如果将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量,这样形成‘闭包’
  43. }
  44. $getMoney = getMoneyFunc();
  45. $getMoney(); // 1
  46. $getMoney(); // 2
  47. $getMoney(); // 3

闭包的好处

1. 减少循环

  1. // 一个基本的购物车,包括一些已经添加的商品和每种商品的数量。
  2. // 其中有一个方法用来计算购物车中所有商品的总价格。该方法使用了一个closure作为回调函数。
  3. class Cart{
  4. const PRICE_BUTTER = 1.00;
  5. const PRICE_MILK = 3.00;
  6. const PRICE_EGGS = 6.95;
  7. protected $products = array();
  8. public function add($product , $quantity)
  9. {
  10. $this->products[$product] = $quantity;
  11. }
  12. public function getQuantity($product)
  13. {
  14. return isset($this->products[$product]) ? $this->products[$product] : false;
  15. }
  16. public function getTotal($tax)
  17. {
  18. $total = 0.00;
  19. // 使用闭包减少循环;
  20. $callback = function($quantity , $product) use ($tax , &$total){
  21. $pricePerItem = constant(__CLASS__ . "::PRICE_" . strtoupper($product));
  22. $total += ($pricePerItem * $quantity) * ($tax + 1.0);
  23. };
  24. array_walk($this->products , $callback);
  25. return round($total , 2);;
  26. }
  27. }
  28. $my_cart = new Cart;
  29. // 往购物车里添加条目
  30. $my_cart->add('butter' , 1);
  31. $my_cart->add('milk' , 3);
  32. $my_cart->add('eggs' , 6);
  33. // 打出出总价格,其中有 5% 的销售税.
  34. print $my_cart->getTotal(0.05) . "\n";
  35. // The result is 54.29

2. 减少函数的参数

  1. function html($code , $id = "" , $class = "")
  2. {
  3. if ($id !== "")
  4. $id = " id = \"{$id}\"";
  5. $class = ($class !== "") ? " class =\"$class\"" : "";
  6. $open = "<$code$id$class>";
  7. $close = "</$code>";
  8. return function($inner = "") use ($open , $close){
  9. return "$open$inner$close";
  10. };
  11. }
  12. $tag = html('div','','class');
  13. // 可读性和可维护性大大提高;
  14. echo $tag('div1,div1,div1');
  15. echo PHP_EOL;
  16. echo $tag('div2,div2,div2');

3. 解除递归函数

  1. // ↓↓ 注意,这里的fib一定要用引用哦,因为第一次的时候就会Notice: Undefined variable,然后后面的fib()就会错误;
  2. $fib = function($n) use (&$fib){
  3. if ($n == 0 || $n == 1)
  4. return 1;
  5. return $fib($n - 1) + $fib($n - 2);
  6. };
  7. echo $fib(2) . "\n"; // 2
  8. $lie = $fib;
  9. $fib = function(){
  10. die('error');
  11. };//rewrite $fib variable
  12. echo $lie(5); // error 达到递归解除;

4. 关于延迟绑定

  1. $result = 0;
  2. $one = function(){
  3. var_dump($result);
  4. };
  5. $two = function() use ($result){
  6. var_dump($result);
  7. };
  8. // 如果使用引用,就能使use里面的变量完成延迟绑定,也就是在调用的时候再赋值;
  9. $three = function() use (&$result){
  10. var_dump($result);
  11. };
  12. $four = function() use ($result){
  13. var_dump($result); //在回调生成的时候进行赋值;
  14. };
  15. $result += 1;
  16. $one(); // outputs NULL: $result is not in scope
  17. $two(); // outputs int(0): $result was copied
  18. $three(); // outputs int(1)
  19. $four(); // outputs int(1)

几个配合回调或闭包的函数

bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )

  1. /**
  2. * @param array $array
  3. * @param callable $funcname ()
  4. * @param mixed|NULL $userdata
  5. * @return bool
  6. * bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )
  7. */
  8. $fruits = array(
  9. "d" => "lemon" ,
  10. "a" => "orange" ,
  11. "b" => "banana" ,
  12. "c" => "apple"
  13. );
  14. $test_print = function(&$item2 , $key, $prefix){
  15. $item2 .= ' 10';
  16. echo "{$prefix} : $key => $item2\n";
  17. };
  18. /*
  19. this result : d => lemon
  20. this result : a => orange
  21. this result : b => banana
  22. this result : c => apple
  23. */
  24. array_walk($fruits , $test_print, 'this result');
  25. print_r($fruits);
  26. /*
  27. Array
  28. (
  29. [d] => lemon 10
  30. [a] => orange 10
  31. [b] => banana 10
  32. [c] => apple 10
  33. )
  34. */

bool array_walk_recursive ( array &$input , callable $funcname [, mixed $userdata = NULL ]

  1. $sweet = array(
  2. 'a' => 'apple' ,
  3. 'b' => 'banana'
  4. );
  5. $fruits = array(
  6. 'sweet' => $sweet ,
  7. 'sour' => 'lemon'
  8. );
  9. $test_print = function($item , $key)
  10. {
  11. echo "$key holds $item\n";
  12. };
  13. array_walk_recursive($fruits , $test_print);
  14. /*
  15. * 自动跳过sweet,因为sweet是数组;任何其值为 array 的键都不会被传递到回调函数中去
  16. a holds apple
  17. b holds banana
  18. sour holds lemon
  19. */

array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

  1. $odd = function($var){
  2. return ($var & 1);
  3. };
  4. $even = function($var){
  5. return (!($var & 1));
  6. };
  7. $array1 = array( "a" => 1 , "b" => 2 , "c" => 3 , "d" => 4 , "e" => 5 );
  8. $array2 = array( 6 , 7 , 8 , 9 , 10 , 11 , 12 );
  9. echo "Odd :\n";
  10. print_r(array_filter($array1 , "odd"));
  11. /*
  12. Odd :
  13. Array
  14. (
  15. [a] => 1
  16. [c] => 3
  17. [e] => 5
  18. )
  19. */
  20. echo "Even:\n";
  21. print_r(array_filter($array2 , "even"));
  22. /*
  23. Even:
  24. Array
  25. (
  26. [0] => 6
  27. [2] => 8
  28. [4] => 10
  29. [6] => 12
  30. )
  31. */
  32. # 如果不传第二参数的的话
  33. $entry = array(
  34. 0 => 'foo',
  35. 1 => false,
  36. 2 => -1,
  37. 3 => null,
  38. 4 => ''
  39. );
  40. print_r(array_filter($entry));
  41. /*
  42. * 当前值为false的话就filter;
  43. Array
  44. (
  45. [0] => foo
  46. [2] => -1
  47. )
  48. */

array array_map ( callable $callback , array $arr1 [, array $array ] )

  1. /**
  2. * @param callable $callback
  3. * @param array $arr1
  4. * @param array $array
  5. */
  6. $func = function($value) {
  7. return $value * 2;
  8. };
  9. print_r(array_map($func, range(1, 5)));
  10. /*
  11. Array
  12. (
  13. [0] => 2
  14. [1] => 4
  15. [2] => 6
  16. [3] => 8
  17. [4] => 10
  18. )
  19. */
  20. $show_Spanish = function($n , $m){
  21. return ("The number $n is called $m in Spanish");
  22. };
  23. $a = array( 1 , 2 , 3 , 4 , 5 );
  24. $b = array( "uno" , "dos" , "tres" , "cuatro" , "cinco" );
  25. $c = array_map($show_Spanish , $a , $b);
  26. /**
  27. print_r($c);
  28. Array
  29. (
  30. [0] => The number 1 is called uno in Spanish
  31. [1] => The number 2 is called dos in Spanish
  32. [2] => The number 3 is called tres in Spanish
  33. [3] => The number 4 is called cuatro in Spanish
  34. [4] => The number 5 is called cinco in Spanish
  35. )
  36. */
  37. $map_Spanish = function($n , $m){
  38. return (array($n => $m));
  39. };
  40. $d = array_map($map_Spanish , $a , $b);
  41. print_r($d);
  42. /**
  43. Array (
  44. [0] => Array ( [1] => uno )
  45. [1] => Array ( [2] => dos )
  46. [2] => Array ( [3] => tres )
  47. [3] => Array ( [4] => cuatro )
  48. [4] => Array ( [5] => cinco )
  49. )
  50. */

mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )

  1. /**
  2. * 用回调函数迭代地将数组简化为单一的结果值,解释不清楚的一看代码就明白了;
  3. * @param array $input
  4. * @param callable $function
  5. * @param mixed|NULL $initial 如果指定了可选参数 initial,该参数将被当成是数组中的第一个值来处理,或者如果数组为空的话就作为最终返回值。
  6. */
  7. $rsum = function($result , $value){
  8. // $result 初始值为NULL, 如果有第三参数的话,第三参数为初始值;
  9. $result += $value;
  10. return $result;
  11. };
  12. $rmul = function($result , $value){
  13. $result *= $value;
  14. return $result;
  15. };
  16. $a = array(1, 2, 3, 4, 5);
  17. $x = array();
  18. $b = array_reduce($a, $rsum); // (NULL)0+1+2+3+4+5 = 15;
  19. $c = array_reduce($a, $rmul, 10); // 10*1*2*3*4*5 = 1200;
  20. $d = array_reduce($x, $rsum, "No data to reduce"); // No data to reduce

mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )

  1. /**
  2. * @param mixed $pattern 正则模式;
  3. * @param callable $callback
  4. * @param mixed $subject
  5. * @param int $limit 对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
  6. * @param int $count 如果指定,这个变量将被填充为替换执行的次数。
  7. * mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )
  8. */
  9. // 将文本中的年份增加一年.
  10. $text = "April fools day is 04/01/2002\n";
  11. $text .= "Last christmas was 12/24/2001\n";
  12. // 回调函数
  13. $next_year = function($matches){
  14. // 通常: $matches[0]是完成的匹配
  15. // $matches[1]是第一个捕获子组的匹配
  16. // 以此类推
  17. return $matches[1] . ($matches[2] + 1);
  18. };
  19. echo preg_replace_callback("|(\d{2}/\d{2}/)(\d{4})|" , $next_year , $text);

mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

mixed call_user_func_array ( callable $callback , array $param_arr )

  1. /**
  2. * @param callable $callback 第一个参数为需要调用的函数; 如果是数组array($classname, $methodname)
  3. * @param mixed $parameter 第二个参数开始就是队列进该函数的参数;
  4. * @param mixed $parameter2
  5. * @param mixed $parameter3
  6. * ..
  7. * @return 返回值:返回调用函数的结果,或FALSE
  8. */
  9. $eat = function($fruit , $num){ //参数可以为多个
  10. echo "You want to eat $fruit $num pcs, no problem\n";
  11. };
  12. call_user_func($eat , "apple" , 10); //print: You want to eat apple 10 pcs, no problem;
  13. call_user_func($eat , "orange" , 5); //print: You want to eat orange 5 pcs,no problem;
  14. // 调用类方法
  15. class myclass {
  16. public static function say_hello($name,$message)
  17. {
  18. echo "Hello! $name $message";
  19. }
  20. }
  21. //array(类名,静态方法名),参数
  22. call_user_func(array('myclass', 'say_hello'), 'dain_sun', 'good person');
  23. call_user_func_array(array('myclass', 'say_hello'), array('dain_sun', 'good person'));
  24. // Hello! dain_sun good person
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注