PHP中的匿名函数和闭包

一:匿名函数 (在php5.3.0 或以上才能使用)

php中的匿名函数, 也叫闭包函数(closures), 允许指定一个没有名称的函数。最常用的就是回调函数的参数值。
匿名函数的定义:

1
2
3
$closureFunc = function(){
return true;
};

eg: 把匿名函数赋值给变量,通过变量来调用

1
2
3
4
5
$closureFunc = function($str){
   echo $str;
};
$closureFunc("hello world!");
// 输出: hello world!

二:闭包
2.1 将匿名函数放在普通函数中,也可以将匿名函数返回,这就构成了一个简单的闭包
1
2
3
4
5
6
7
8
function closureFunc1(){
$func = function(){
echo "hello";
};
$func();
}
closureFunc1();
//输出: hello
2.2 在匿名函数中引用局部变量
1
2
3
4
5
6
7
8
9
function closureFunc2(){
$num = 1;
$func = function(){
echo $num;
};
$func();
}
closureFunc2();
//Notice: Undefined variable: num

如果经常写js代码看到这个的话应该会感到很奇怪,因为类似的代码放在js里面一切都是那么的合理,根本就没有报错的理由。那是因为js语言是自动封装应用状态的,在php中,必须手动调用bindTo()方法或者使用use关键字把状态附加到php闭包中。

1
2
3
4
5
6
7
8
9
10
function enclosePerson ($name) {
return function ($doCommand) use ($name) {
return sprintf('%s, %s', $name, $doCommand);
};
}
// 把字符串“Clay”封装在闭包中
$clay = enclosePerson('Clay');
// 传入参数,调用闭包
echo $clay('get me sweet tea!');
// 输出 --> “Clay, get me sweet tea!”

可以看到在生成闭包的时候把变量附加到闭包中,那么在调用闭包时那个变量值依然存在并且不能被改变。

php的闭包是对象,与其他php对象类似,每个闭包实例都可以使用 $this 关键字来获取闭包内部状态,闭包的大部分状态是没什么用的,但有一个bindTo()方法为闭包添加一些有趣的潜力,可以使用这个方法把Closure对象的内部状态绑定到其他对象上。没看懂是吗?我来用通俗易懂的话来说一遍,通过这个bindTo()方法可以把其他对象的$this绑定到闭包对象中,那么就能在闭包对象中访问到被绑定的对象的受保护的属性。举个栗子:

下面的例子是一些框架中路由实现的mini版,仅为说明bindTo()的作用及用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class App 
{
protected $routes = array();
protected $responseStatus = '200 OK';
protected $responseContentType = 'text/html';
protected $responseBody = 'hello word';

public function addRoute($routePath, $routeCallback)
{
$this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
}

public function dispatch($currentPath)
{
foreach ($this->routes as $routePath => $callback){
if ($routePath === $CurrentPath) {
$callback();
}
}
header('HTTP/1.1' . $this->$responseStatus);
header('Content-type:' . $this->$responseContentType);
header('Content-length:' . $this->$responseBody);
echo $this->$responseBody;
}
}

可以看到,addRoute方法是用来添加(注册)路由的,注册的同时会要求传如一个闭包函数,那么在注册的同时就通过bindTo()方法绑定了App实例的$this,bindTo()方法第二个参数的作用就是指定需要绑定闭包对象的那个实例所属的PHP类。

1
2
3
4
5
6
$app = new App();
$app->addRoute('/user/info', function(){
$this->$responseContentType = 'application/json;charset=utf8';
$this->$responseBody = '{'name': 'july'}';
});
$app->dispatch('/user/info');