概述
闭包是指在创建时封装周围状态的函数,即使闭包所在的环境的不存在了,闭包中封装的状态依然存在。
匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何PHP
函数对象那样传递。不过匿名函数仍然是函数,因此可以调用,还可以传入参数,适合作为函数或方法的回调。
但在 php
中,闭包与匿名函数被视为相同的概念。
Closure
是代表闭包(匿名函数)的预定义类,该类定义了闭包的一些操作,例如bind
、bindTo
等方法。
类定义:
1
2
3
4
5
6
7
|
Closure {
private __construct ( void )
public static bind ( Closure $closure , object $newthis [, mixed $newscope = "static" ] ) : Closure
public bindTo ( object $newthis [, mixed $newscope = "static" ] ) : Closure
public call ( object $newthis [, mixed $... ] ) : mixed
public static fromCallable ( callable $callable ) : Closure
}
|
bindTo、bind 方法
bindTo
,非静态方法,复制当前闭包对象,绑定到新的类对象、类作用域,并返回新的闭包。
bind
,静态方法。复制一个闭包,绑定指定的类对象和类作用域,并返回绑定好的闭包。
解释两个参数:newthis
、newscope
newthis
新的 this
对象。若想将闭包绑定到某个对象上,需指定此参数。否则传 null
。
经常可以看到一些框架的路由定义如下:
1
2
3
|
$app->get('/test', function () {
echo $this->request->getMethod();
});
|
在闭包中可以使用 this
关键字?
这是因为该闭包被绑定了 this
对象。绑定 this
对象后,就可以访问该对象的 public
属性、方法。若要访问非 public
方法、属性,则需绑定作用域。
newscope
新的作用域。当访问非 public
的方法、属性时,需指定作用域。
绑定新作用域的作用如下例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?php
class Container
{
protected static $name = 'container';
}
$clo1 = function() {
var_dump(Container::$name);
};
$clo2 = Closure::bind($clo1,null,Container::class);
$clo1();
$clo2();
|
输出的接口中,$clo1()
可以输出 name
静态变量的值,而$clo2()
却直接报错。
这是因为 $clo1
的作用域与 container
中的 name
作用域不同,且 name
又是一个非 public
变量。因此 $clo
闭包中访问 Container::$name
就像直接从类外访问 name
,肯定会报错的。
而 $clo2
通过 bind
绑定了 container
类作用域,$clo2
中访问 name
就像在 container
类中访问 name
。
因此,若想访问某个对象的非静态非公有属性、方法,则需绑定 newthis
、newscope
,代码示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?php
class Test
{
protected function getName()
{
return 'name';
}
}
$clo = function() {
var_dump($this->getName());
};
Closure::bind($clo,new Test(), Test::class)(); // 输出 name
Closure::bind($clo,new Test())(); // 报错:Fatal error: Uncaught Error: Call to protected method Test::getName() from context 'Closure'
|
bindTo、bind 方法的区别
bind
相当于 bindTo
的静态方法版,实现的功能上是一样的。
call 方法
1
|
$clo->call($obj, ...$params);
|
等价于
1
2
|
$clo->bindTo($obj, $obj);
$clo(...$params);
|
验证示例(改编自 PHP 官网):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
class Value {
protected $value;
public function __construct($value) {
$this->value = $value;
}
private function getValue() {
return $this->value;
}
}
$three = new Value(3);
$four = new Value(4);
$closure = function ($delta) {
var_dump($this->getValue() + $delta);
};
$closure->call($three, 4);
$closure->call($four, 4);
|
fromCallable 方法
把 callable
类型的东西变成闭包,如果传入的参数不是 callable
的,则报错。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?php
class Foo
{
protected $num = 1;
public static function hello(string $bar)
{
echo 'hello ' . $bar;
}
}
$hello = Closure::fromCallable(['Foo', 'hello']);
$hello('world');
Closure::fromCallable(['Foo', 'a']); // 报错
|
总结
个人感觉闭包和类都挺像的,都是为了复用。只不过实现的角度不同,一个是面向对象,一个是面向过程的。
参考资料:
https://juejin.im/post/5b8129aa51882542f03807a8
https://stackoverflow.com/questions/57325088/difference-between-calling-a-closure-in-class-via-call-user-func-and-call