#在PHP中应用回调例子
如果对js非常熟悉的话,一定对匿名函数,回调等概念不陌生
```
$("#username").clcik(function(){
//code
$getJSON('/data.php', {name:name}, function(){
//远程获取数据
});
});
```
相信写js的人对上面的代码一定不陌生,同样,PHP之道的发起人写的`slim`框架,也在大量使用类似的语法,比如下面一段slim的代码
```
$app->get('/', function ($request, $response, $args) {
return $response->write("Hello " . $args['name']);
});
```
是不是写php跟写jQuery似的,非常有意思,Slim的作者是PHP和JavaScript程序员,同时也是《PHP The Right Way》的作者,相信js工程师也一定非常喜欢这样的语法。
#PHP创建闭包
注意,在PHP中,匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。比如官网一个闭包函数的例子
```
echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');
// 输出 helloWorld
```
闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。把一个 closure 对象赋值给一个`变量的方式`与普通变量赋值的语法是一样的,最后也要加上分号:
```
$add = function($a, $b){
return $a + $b;
};
echo $add(3,4);//输出7
```
注意,闭包函数构建了自己个一个作用域,如果需要使用父级作用域中的变量,必须使用`use`关键字
```
$message = 'hello';
$say1 = function(){
echo $message;
};
$say2 = function() use ($message){
echo $message;
}
$say1();//PHP Notice: Undefined variable: message
$say2();//hello
```
#闭包的本质
闭包和匿名函数其实就是伪装成函数的对象,如果审查PHP闭包或者匿名函数,你会发现它就是Closure类的实例,比如创建这个闭包
```
$add = function($a, $b){
return $a + $b;
};
echo $add(3,4);//输出7
```
闭包的情况是:
1. 创建一个闭包对象,这个对象继承Closure类
2. 实现Closure类中的__invoke()方法
2. 把闭包赋值给$add对象
3. 只要变量名后面有(),就立马调用__invoke()方法
比如下面这个例子证明闭包就是Closure类的实现
```
$add = function($a, $b){
return $a + $b;
};
var_dump($add instanceof Closure);//输出true
echo $add->__invoke(3,4);//输出7
```
或者再看一个例子,()会触发类的__invoke()方法
```
class Func{
function __invoke(){
echo 8888;
}
}
$func = new Func();
$func();//输出8888
```
#闭包实现路由回调
前面说到闭包有一个__invoke()魔术方法,但是,闭包还有一个方法,叫bindTo().这个方法可以吧Closure对象的内部状态绑定到其他对象上。bindTo方法的第二个参数很重要,作用是指定绑定闭包的那个对象所属的PHP类。基于这一点,我们可以使用bindTo()方法把路由URL映射到匿名回调函数上,框架会把匿名函数绑定到应用对象上,这么做之后就可以在匿名函数中使用$this关键字引用重要的应用对象
```
class App{
protected $routes = array();
protected $responseStatus = 200;
protected $responseContentType = 'text/html';
protected $responseBody = '';
public function addRoute($path, $call){
/**
* 第一个参数给匿名函数的一个对象,或者 NULL 来取消绑定。这里表示当前对象App
* 第二个参数关联到匿名函数的类作用域,这会决定绑定的对象的 保护、私有成员 方法的可见性,这一项保持可以访问protected属性
*/
$this->routes[$path] = $call -> bindTo($this, __CLASS__);
}
public function dispatch($curr_path){
foreach($this->routes as $path => $call){
if($path == $curr_path){
//执行回调函数
$call();
}
}
header('HTTP/1.1'. $this->responseStatus);
header('Content-type: '. $this->responseContentType);
header('Content-length: '. mb_strlen($this->responseBody));
echo $this->responseBody;
}
}
$app = new App();
$app -> addRoute('/', function(){
//由于这个匿名函数关联到了App类,这里的$this指向App这个实例
$this->responseContentType = 'application/json';
$this->responseBody = '{"name":"yongxiong"}';
});
$app -> dispatch('/');
```
测试这个路由App,使用PHP启动服务器
```
php -S localhost:9000
```
可以看到以下访问结果