说到闭包,就不得不说一下匿名函数。匿名函数是没有名称的函数,可以赋值给变量,本身作为参数传递在函数之间,可以调用、传参,常用于回调之中。闭包可以理解为就是匿名函数,在创建时封装上下文的函数,即使是闭包创建时的环境不存在了,其封装的上下文依然存在。
闭包的父作用域是定义该闭包的函数。闭包函数构建了自己的作用域,如果想要使用父作用域的变量,需要通过 use 语言结构传递进去。需要注意:这些变量必须在函数或者类的头部定义。
PHP 会自动把闭包转换成内置类 Closure 的对象实例,因此闭包变量也拥有Closure的bindTo()方法。
我们这里实现一个路由类:
1 <?php
2 class App {
3 protected $routes = [];
4 protected $responseStatus = '200 OK';
5 protected $responseContentType = 'text/html';
6 protected $responseBody = 'Hello world!';
7
8 public function addRoute($routePath, $routeCallback) {
9 $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
10 }
11
12 public function dispatch($k) {
13 if (isset($this->routes[$k])) {
14 $this->routes[$k]();
15 }
16 header('Content-Type: ' . $this->responseContentType);
17 echo $this->responseBody, "\n";
18 }
19 }
20
21 $app = new App();
22 $app->addRoute('home/index', function() {
23 $this->responseContentType = 'application/json;charset=utf8';
24 $this->responseBody = '{"page":200}';
25 });
26 $app->dispatch('home/index');
说明:
line 9:bindTo() 复制当前闭包对象,返回一个新的闭包对象,新的闭包绑定指定的$this对象和类作用域
line 23 ~ 24:$this 是Class App 的实例对象,
启动测试服务:
1 ➜ Desktop php -S 127.0.0.1:8800
2 PHP 7.0.17 Development Server started at Wed Aug 23 23:34:49 2017
3 Listening on http://127.0.0.1:8800
4 Document root is /Users/kevhu/Desktop
5 Press Ctrl-C to quit.
curl 请求测试:
1 ➜ ~ curl -v http://127.0.0.1:8800/app.php
2 * Trying 127.0.0.1...
3 * TCP_NODELAY set
4 * Connected to 127.0.0.1 (127.0.0.1) port 8800 (#0)
5 > GET /app.php HTTP/1.1
6 > Host: 127.0.0.1:8800
7 > User-Agent: curl/7.54.0
8 > Accept: */*
9 >
10 < HTTP/1.1 200 OK
11 < Host: 127.0.0.1:8800
12 < Connection: close
13 < X-Powered-By: PHP/7.0.17
14 < Content-Type: application/json;charset=utf8
15 <
16 {"page":200}
17 * Closing connection 0
注意
line 14:是回调函数中设置的responseContentType 返回值
line 16: 是回调函数中设置的responseBody返回值
这里遗留一个问题:
关于Closure::__invoke(), 手册上说,它与调用闭包的过程无关。这里理解不了其含义,后续再跟进解决这个疑问。