闭包和匿名函数是从php 5.3.0开始出现的,这是我最喜欢也是用的最多的php功能。听到这些名称心里特别没底(至少我第一次听到时是这么认为的),但是事实上真的很好理解。它们是每个php开发者们的工具箱中必备的最有用的工具。
闭包作为一个函数,在创建时会封装外部的状态。即使最初创建闭包时的环境已经不存在了,封装的状态也会一直保存在闭包中。这是一个不太好掌握的概念,一旦你能够弄明白,感觉就像人生翻开了新的篇章。
匿名函数实际上就是没有名字的函数。匿名函数可以被赋值给变量,像所有其它的php对象一样在代码中传递。但是它终归还是函数,所以你可以调用它并且传递参数。匿名函数最大的用处是作为函数或者方法的回调。
闭包和匿名函数理论上是不同的概念。然而,php认为它们是一码事。所以,当我说闭包的时候也可能指的是匿名函数,反之亦然。
php的闭包和匿名函数在语法上和函数一样,但是别被它们弄混。他们实际上是伪装成函数的对象。如果你打印检查一个php闭包或者匿名函数的类型,你会发现它们都是closure类的实例。closure可以看作是同string和integer一样重要的数据类型。
创建
我们都知道php的闭包和函数看起来很像。当你像例子 2-19那样创建一个php闭包后,你就不会感到惊讶了。
例子 2-19 简单的闭包
hello josh
就这么简单。例子 2-19创建了一个closure对象并将它赋值给变量$closure。它看起来像一个标准的php函数:它使用了相同的语法、接收参数并且有返回值。但是它没有名字。
我们可以调用$closure变量,因为$closure的是一个闭包,closure闭包对象都实现了\_invoke()这个魔术方法。在变量名跟着一对()符号时php会自动查找并调用__invoke()方法。
我通常使用php的闭包对象作为函数和方法的回调。很多php的函数都会使用回调函数,例如array_map()和preg_replace_callback()。这就像为php匿名函数量身定做的功能!记住,就像其它任何值一样,闭包可以像参数一样被传递给其它php函数。在例子 2-10中我使用一个闭包对象作为array_map()函数的回调参数。
例子 2-20 array_map闭包
[2,3,4]
看起来并不是那么让人印象深刻是吗?但是记住,在闭包功能出现之前要实现这样的功能,php开发者们并没有什么好的选择,他们只能创建一个具名函数并把函数名作为参数传递进去才行。这样做执行上会有些慢,最重要的是它分离了回调的实现和使用。老派的php开发者们使用下面的代码:
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: ' . mb_strlen($this->responsebody)); echo $this->responsebody; }}
注意addroute方法。它接收一个路由地址(例如 /users/josh)和一个路由的回调。dispatch()方法接收一个当前http请求地址并调用对应的路由回调。神奇的地方在第11行,我们将路由的回调绑定给了当前app类的实例。这样我们就可以创造一个可以操作app实例状态的回调函数了:
addroute('/users/josh', function () { $this->responsecontenttype = 'application/json;charset=utf8'; $this->responsebody = '{name: josh}';});$app->dispatch('/users/josh');