Home > Article > Backend Development > What are anonymous functions and closures in PHP? How to create?
The content of this article is to introduce to you what anonymous functions and closures are in PHP? How to create anonymous functions and closures? It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Overview
Closures and anonymous functions were introduced in PHP 5.3.0. These two features are very useful and every PHP developer should master them.
Anonymous functions are actually functions without names. Anonymous functions can be assigned to variables and passed like any other PHP function object. However, anonymous functions are still functions, so they can be called and parameters can be passed in, making them suitable as callbacks for functions or methods.
A closure refers to a function that encapsulates the surrounding state when it is created. Even if the environment where the closure is located no longer exists, the state encapsulated in the closure still exists.
Creating anonymous functions
Creating anonymous functions is very simple:
//将匿名函数赋给一个变量,通过变量名+()的形式来调用 $greet = function () { return "Hello World"; }; echo $greet();
Result printing:
Hello World
Anonymous functions are very similar to ordinary PHP functions: the commonly used syntax is the same, they also accept parameters, and can return values. However, closures do not have function names.
Note: The reason why we can call the$greet
variable is because the value of this variable is a closure, and the closure object implements the__invoke()
magic method. As long as there is () after the variable name, PHP will find and call the__invoke
method.
$greet
变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke()
魔术方法,只要变量名后有(),PHP就会查找并调用__invoke
方法。我们通常把匿名函数当做函数或方法的回调使用,事实上,很多PHP函数都会用到匿名函数,比如array_map
和preg_replace_callback
,这是使用PHP匿名函数的绝佳时机。记住,闭包和其他值一样,可以作为参数传入其他PHP函数:
$numberPlusOne = array_map(function ($number) { return $number += 1; }, [1, 2, 3]); print_r($numberPlusOne);
在匿名函数出现之前,要实现这样的功能,PHP开发者只能单独创建具名函数,然后使用名称引用这个函数:
function incrementNumber ($number) { return $number += 1; } $numberPlusOne = array_map(‘incrementNumber’, [1, 2, 3]); print_r($numberPlusOne);
这样做把回调的实现和使用场所隔离开了,而且使用闭包实现代码更加简洁。
创建闭包
包含自由变量的函数与为所有这些自由变量提供了变量绑定的环境一起,被称为闭包。
function makeHelloWorld($name) { $i = 0; return function()use($name, &$i){ echo $name.$i. ' <br>'; $i++; }; } $hello1 = makeHelloWorld("itbsl"); $hello2 = makeHelloWorld("kevin"); $hello1(); $hello1(); $hello1(); $hello2();
打印结果:
itbsl0 itbsl1 itbsl2 kevin0
从父作用域继承变量
在PHP中必须手动调用闭包对象的bindTo
方法或使用use
关键字把父作用域的变量及状态附加到PHP闭包中。而实际应用中,又以使用use
关键字实现居多。
use关键字
实际上,Laravel框架中也大量使用了闭包,最常见的比如路由定义:
Route::group(['domain' => '{account}.myapp.com'], function () { Route::get('user/{id}', function ($account, $id) { // }); });
这里面的两个function都是匿名函数。而从父作用域继承变量的使用场景在Laravel底层源码中也是俯拾即是,比如Model.php(IlluminateDatabaseEloquent
)的saveOrFail
方法:
该方法的作用是使用事务将模型数据保存到数据库,这里面我们使用匿名函数返回保存状态,同时使用use关键字将父作用域的$options
传递给该闭包以便其能够访问这个数据。
此外,还支持传递多个父作用域变量到匿名函数,比如还是在Model
类中的forceFill
方法:
多个变量以逗号分隔即可。
bindTo方法
我们在前面已经提到,闭包是一个对象,所以我们可以在闭包中使用$this关键字获取闭包的内部状态,闭包对象的默认状态没什么用,需要注意的是其中的__invoke
魔术方法和bindTo
方法。
__invoke
的作用前面已经说过,当尝试以调用函数的方式调用一个对象时,__invoke()
方法会被自动调用。
接下来我们来看看bindTo
方法,通过该方法,我们可以把闭包的内部状态绑定到其他对象上。这里bindTo
方法的第二个参数显得尤为重要,其作用是指定绑定闭包的那个对象所属的PHP类,这样,闭包就可以在其他地方访问邦定闭包的对象中受保护和私有的成员变量。
你会发现,PHP框架经常使用bindTo
方法把路由URL映射到匿名回调函数上,框架会把匿名回调函数绑定到应用对象上,这样在匿名函数中就可以使用$this
We usually use anonymous functions as callbacks of functions or methods. In fact, many PHP functions use anonymous functions, such as array_map
and preg_replace_callback
. is an excellent time to use PHP anonymous functions. Remember, closures, like other values, can be passed as parameters to other PHP functions:
class App { protected $routes = []; protected $responseStatus = '200 OK'; protected $responseContentType = 'text/html'; protected $responseBody = 'Laravel学院'; 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: ' . mb_strlen($this->responseBody)); echo $this->responseBody; } }🎜 Before the advent of anonymous functions, to implement such functionality, PHP developers could only create a separate named function and then reference this function by name :🎜
$app = new App(); $app->addRoute(‘user/nonfu’, function(){ $this->responseContentType = ‘application/json;charset=utf8’; $this->responseBody = ‘{“name”:”LaravelAcademy"}'; }); $app->dispatch(‘user/nonfu');🎜This isolates the implementation of callbacks from the places of use, and the use of closures to implement code is more concise. 🎜🎜🎜🎜Creating Closures 🎜🎜🎜🎜A function containing free variables, together with an environment that provides variable bindings for all these free variables, is called a closure. rrreee🎜Print results:🎜rrreee🎜🎜Inherit variables from the parent scope🎜🎜🎜In PHP, you must manually call the
bindTo
method of the closure object or use use
Keyword attaches the variables and state of the parent scope to the PHP closure. In actual applications, most of them are implemented using the use
keyword. 🎜🎜🎜use keyword 🎜🎜🎜🎜In fact, closures are also widely used in the Laravel framework. The most common ones are routing definitions: 🎜rrreee🎜Here are two Each function is an anonymous function. The usage scenarios of inheriting variables from the parent scope are also common in Laravel's underlying source code, such as the saveOrFail
method of Model.php (IlluminateDatabaseEloquent
): 🎜🎜🎜🎜The function of this method is to use transactions to transfer model data Save to the database, here we use an anonymous function to return the saved status, and use the use keyword to pass the $options
of the parent scope to the closure so that it can access the data. 🎜🎜In addition, it also supports passing multiple parent scope variables to anonymous functions, such as the forceFill
method in the Model
class: 🎜🎜🎜 Multiple variables can be separated by commas. 🎜🎜🎜bindTo method 🎜🎜🎜🎜We have mentioned before that the closure is an object, so we can use the $this keyword in the closure to get the closure The internal state of the closure object is useless. What needs to be noted is the 这里我们需要重点关注 在Larval底层也有用到 总结:以上就是本篇文章的全部内容,希望能对大家的学习有所帮助。__invoke
magic method and the bindTo
method. 🎜🎜The role of __invoke
has been mentioned before. When trying to call an object by calling a function, the __invoke()
method will be automatically called. 🎜🎜Next let’s take a look at the bindTo
method. Through this method, we can bind the internal state of the closure to other objects. The second parameter of the bindTo
method here is particularly important. Its function is to specify the PHP class to which the object of the bound closure belongs. In this way, the closure can access the bound closure in other places. Protected and private member variables in an object. 🎜🎜You will find that the PHP framework often uses the bindTo
method to map routing URLs to anonymous callback functions. The framework will bind the anonymous callback function to the application object, so that it can be used in the anonymous function. The $this
keyword refers to important application objects: 🎜class App {
protected $routes = [];
protected $responseStatus = '200 OK';
protected $responseContentType = 'text/html';
protected $responseBody = 'Laravel学院';
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: ' . mb_strlen($this->responseBody));
echo $this->responseBody;
}
}
addRoute
方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch
方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:$app = new App();
$app->addRoute(‘user/nonfu’, function(){
$this->responseContentType = ‘application/json;charset=utf8’;
$this->responseBody = ‘{“name”:”LaravelAcademy"}';
});
$app->dispatch(‘user/nonfu');
bindTo
方法,详见Illuminate\Support\Traits\Macroable
的__call
方法:闭包和匿名函数(https://laravelacademy.org/post/4341.html)