通过几个栗子认识PHP闭包

2018-12-26 19:31:11   PHP

  PHP闭包  

<!-- TOC -->

<!-- /TOC -->

有收获的话请加颗小星星,没有收获的话可以 反对 没有帮助 举报三连

  • 示例代码
  • 本文地址
  • 本人能力有限,如遇到什么不对的地方还望指出修正,谢谢
  • 所有栗子的输出都使用symfony/var-dumpe美化了

匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。

匿名函数目前是通过 Closure 类来实现的。

一、栗子1 用作于回调

  1. $rs = preg_replace_callback('/-([a-z])/', function ($match) {
  2. return strtoupper($match[1]);
  3. }, 'hello-world');
  4. dump($rs); // "helloWorld"

二、栗子2 用作于变量赋值

  1. $greet = function ($name) {
  2. dump($name);
  3. };
  4. dump($greet instanceof Closure); // true
  5. $greet('PHP'); // "PHP"

三、栗子3 从父作用域继承变量

  1. $message = 'hello';
  2. $example = function () use ($message) {
  3. dump($message);
  4. };
  5. dump($example instanceof Closure); // true
  6. $example(); // "hello"

四、栗子4的前提条件,简单理解call_user_func_array()call_user_func()方法

1. call_user_func — 把第一个参数作为回调函数调用

function call_user_func ($function, …$parameter) {}

该方法接收多个参数,第一个就是回调函数,可以是普通函数,也可以是闭包函数,后面的多个参数都是作为函数回调使用

  1. $rs = call_user_func(function (...$params) {
  2. return func_get_args();
  3. }, 1, 2, 3);
  4. dump($rs); // [1,2,3]

2. call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

function call_user_func_array ($function, array $param_arr) {}

该方法接收2个参数,第一个就是回调函数,可以是普通函数,也可以是闭包函数,后面的数组参数都是作为函数回调使用

  1. $rs = call_user_func_array(function (array $params) {
  2. return func_get_args();
  3. }, [1, 2, 3]);
  4. dump($rs); // [1,2,3]

五、栗子4 绑定闭包在指定对象

楼主见解是将方法绑定到指定类上,使得方法也可以使用类的属性和方法,非常适合配合__call()魔术方法和call_user_func_array方法一起使用

1. Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。

function bindTo($newthis, $newscope = ‘static’) { }

  1. <?php
  2. namespace PHP\Demo\Closure;
  3. class ClosureBindTo
  4. {
  5. public function __call($name, $arguments)
  6. {
  7. if (count($arguments) > 1 && $arguments[0] instanceof \Closure) {
  8. return call_user_func_array($arguments[0]->bindTo($this), array_slice($arguments, 1));
  9. }
  10. throw new \InvalidArgumentException("没有这个方法");
  11. }
  12. }
  13. // 测试
  14. public function testClosureBindTo()
  15. {
  16. $obj = new ClosureBindTo();
  17. $this->assertEquals(2, $obj->add(function (array $params) {
  18. return ++$params[0];
  19. }, [1]));
  20. // 测试同一个实例
  21. $newObj = $obj->test(function (array $params){
  22. return $this;
  23. }, [1]);
  24. $this->assertTrue($newObj instanceof $obj);
  25. }

2. Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。

static function bind(Closure $closure, $newthis, $newscope = ‘static’) { }

bind函数是bindTo的静态表示

  1. <?php
  2. namespace PHP\Demo\Closure;
  3. class ClosureBind
  4. {
  5. private $methods = [];
  6. public function addMethod(string $name, \Closure $callback)
  7. {
  8. if (!is_callable($callback)) {
  9. throw new \InvalidArgumentException("第二个参数有误");
  10. }
  11. $this->methods[$name] = \Closure::bind($callback, $this, get_class());
  12. }
  13. public function __call(string $name, array $arguments)
  14. {
  15. if (isset($this->methods[$name])) {
  16. return call_user_func_array($this->methods[$name], $arguments);
  17. }
  18. throw new \RuntimeException("不存在方法[{$name}]");
  19. }
  20. }
  21. // 测试
  22. public function testClosureBind()
  23. {
  24. $obj = new ClosureBind();
  25. $obj->addMethod('add', function (array $params) {
  26. return ++$params[0];
  27. });
  28. $this->assertEquals(2, $obj->add([1]));
  29. // 测试同一个实例
  30. $obj->addMethod('test', function (array $params) {
  31. return $this;
  32. });
  33. $this->assertTrue($obj->test([1]) instanceof $obj);
  34. }

六、参考资料