匿名函数,也叫闭包函数,说白了就是“没有名字的函数”,和通常函数结构同样,只是少了函数名以及最后须要加上分号;
。php
注:理论上讲闭包和匿名函数是不一样的概念,不过PHP将其视做相同的概念。
$func = function() { echo 'Hello World' . PHP_EOL; }; $func();
匿名函数和普通函数的区分有:html
$message = 'hello'; $example = function () use ($message) { return $message; }; $message = 'world'; echo $example(); 输出:hello
注意:必须使用use
关键字将变量传递进去才行,具体见官方文档。json
定义一个闭包函数,其实就是实例化一个闭包类(Closure
)对象:闭包
$func = function() { echo 'hello world' . PHP_EOL; }; var_dump($func); 输出: object(Closure)#1 (0) { }
类摘要:app
Closure { __construct ( void ) public static Closure bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] ) public Closure bindTo ( object $newthis [, mixed $newscope = 'static' ] ) }
除了以上方法,闭包还实现了一个__invoke()
魔术方法,当尝试以调用函数的方式调用一个对象时,__invoke()
方法会被自动调用。框架
接下来咱们来看看bindTo
方法,经过该方法,咱们能够把闭包的内部状态绑定到其余对象上。这里bindTo
方法的第二个参数显得尤其重要,其做用是指定绑定闭包的那个对象所属的PHP类,这样,闭包就能够在其余地方访问绑定闭包的对象中受保护和私有的成员变量。函数
你会发现,PHP框架常用bindTo
方法把路由URL映射到匿名回调函数上,框架会把匿名回调函数绑定到应用对象上,这样在匿名函数中就能够使用$this
关键字引用重要的应用对象:学习
class App { protected $routes = []; protected $responseStatus = '200 OK'; protected $responseContentType = 'text/html'; protected $responseBody = 'Hello World'; public function addRoute($path, $callback) { $this->routes[$path] = $callback->bindTo($this, __CLASS__); } public function dispatch($path) { foreach ($this->routes as $routePath => $callback) { if( $routePath === $path) { $callback(); } } header('HTTP/1.1 ' . $this->responseStatus); header('Content-Type: ' . $this->responseContentType); header('Content-Length: ' . mb_strlen($this->responseBody)); echo $this->responseBody; } }
这里咱们须要重点关注addRoute
方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch
方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,咱们将路由回调绑定到了当前的App实例上。这么作可以在回调函数中处理App实例的状态:this
$app = new App(); $app->addRoute(‘/user’, function(){ $this->responseContentType = ‘application/json;charset=utf8’; $this->responseBody = '世界你好'; }); $app->dispatch('/user');
匿名函数能够从父做用域继承变量,而这个父做用域是定义该闭包的函数(不必定是调用它的函数)。spa
利用这个特性,咱们能够实现一个简单的控制反转IoC
容器:
class Container { protected static $bindings; public static function bind($abstract, Closure $concrete) { static::$bindings[$abstract] = $concrete; } public static function make($abstract) { return call_user_func(static::$bindings[$abstract]); } } class talk { public function greet($target) { echo 'Hello ' . $target->getName(); } } class A { public function getName() { return 'World'; } } // 建立一个talk类的实例 $talk = new talk(); // 将A类绑定至容器,命名为foo Container::bind('foo', function() { return new A; }); // 经过容器取出实例 $talk->greet(Container::make('foo')); // Hello World
上述例子中,只有在经过make
方法获取实例的时候,实例才被建立,这样使得咱们能够实现容器。
在Laravel
框架底层也大量使用了闭包以及bindTo
方法,利用好闭包能够实现更多的高级特性如事件触发等。
以上为闭包学习笔记,部分参考了网上的一些文章。