Having been in Web development for a while, we all know or understand that there is a very powerful syntax in JavaScript, and that is closures. In fact, closure functions have long been available in PHP. Closure functions have been around since version 5.3 of PHP. By 7 and later in modern frameworks, the use of closures has become even more ubiquitous. Here, let’s start with the basics of using closures in PHP.

Closures in PHP are all converted to instances of the Closure class. If you assign a value to a variable, you need to add curly braces at the end of the definition; A semicolon. Closures inherit variables from their parent scope, and any such variables should be passed in using the use construct. Since PHP 7.1, you cannot pass variables such as superglobals, $this, or the same name as the argument

Basic grammar

Closures are very simple to use and very similar to JavaScript. Because they all have another alias, called anonymous functions.

 1$a = function() {2echo "this is testA";
 4$a(a); // this istestA
 7function testA ($a) {
 8    var_dump($a); 
10testA($a); // class Closure# 1 (0) {}11 to 12$b = function ($name13) {echo 'this is ' . $name; 14}; 15 to 16$b('Bob'); // this is Bob
Copy the code

We directly assign the variables $a and $b to two functions. This allows us to call both functions using a variable (). With the testA() method, we can see that the Closure function can be passed as a normal argument because it is automatically converted to an instance of the Closure class.

 1$age = 16;
 2$c = function ($name3) {echo 'this is ' . $name . ', Age is ' . $age; 4}; 5 and 6$c('Charles'); // this is Charles, Age is
 8$c = function ($name) use ($age9) {echo 'this is ' . $name . ', Age is ' . $age; 10}; 11 to 12$c('Charles'); // this is Charles, Age is 16
Copy the code

If we need to call an external variable, we need to use the use keyword to reference the external variable. This is different from normal functions because closures have strict scoping problems. For global variables, we can use either use or global. However, only use can be used for local variables (variables within a function). We’ll come back to that later.


 1function testD(){
 2    global $testOutVar;
 3    echo $testOutVar;
 5$d = function () use ($testOutVar6) {echo $testOutVar;
 8$dd = function () {
 9    global $testOutVar;
10    echo $testOutVar;
12$testOutVar = 'this is d';
13$d(a); // NULL 14testD(); // this is d
15$dd(a); // this is d 16 17$testOutVar = 'this is e';
18$e = function () use ($testOutVar19) {echo $testOutVar;
21$e(a); // this is e 22 23$testOutVar = 'this is ee';
24$e(a); // this is e 25 26$testOutVar = 'this is f';
27$f = function () use (&$testOutVar28) {echo $testOutVar;
30$f(a); // this is f 31 32$testOutVar = 'this is ff';
33$f(a); // this is ffCopy the code

In scope, the variables passed by use must be defined before the function is defined, as shown in the example above. If the closure ($d) was defined before the variable ($testOutVar), then the variable passed in by use in $d is empty. Similarly, we use global to test that $testOutVar is normal for both the normal function (testD()) and the closure function ($dd).

Variables in the $e function that are modified after the function definition do not affect variables in the $e closure. In this case, you must use pass-by-reference ($f) to change the variables in the closure. This is the same concept as passing by reference and passing by value for ordinary functions.

Except for the use of variables, closures are almost identical to normal functions, such as class instantiation:

1class G
3$g = function () {
4    global $age;
5    echo $age; / / 16 June$gClass = new G();
7    var_dump($gClass); // G info
9$g(a);Copy the code

Class scope

With regard to global scope, there is little difference between a closure function and a normal function. The main difference is the state of use when passing variables as a bridge. Is there anything different in a class method?

 1$age = 18;
 2class A
 4    private $name = 'A Class';
 5    public function testA()
 6    {
 7        $insName = 'test A function';
 8        $instrinsic = function () {
 9            var_dump($this); // this info
10            echo $this->name; // A Class
11            echo $age; // NULL
12            echo $insName; // null
13        };
14        $instrinsic(a); 15 to 16$instrinsic1 = function () {
17            global $age.$insName;
18            echo $age; / / 18 19echo $insName; // NULL
20        };
21        $instrinsic1(a); 22 23 global$age;
24        $instrinsic2 = function () use ($age.$insName25) {echo $age; / / 18 26echo $insName; // test A function27}; 28$instrinsic2();
30    }
33$aClass = new A();
Copy the code

  • The $insName variable in the testA() method is available only through use.

  • The $this in the closure function is the context in which it is called, which in this case is class A itself. The parent scope of a closure is the function that defines the closure (not necessarily the function that calls it). Static closures cannot get $this.

  • Global variables can still be obtained using global.


With these features of closures in mind, let’s look at a few tips:

 1$arr1= [2 ['name'= >'Asia'3 [],'name'= >'Europe'4 [],'name'= >'America'], 5]; 6, 7$arr1Params = ' is good! ';
 8// foreach($arr1 as $k= >$a9) {/ /$arr1[$k] = $a . $arr1Params;
10// }
11// print_r($arr1);
13array_walk($arr1.function (&$v) use ($arr1Params14) {$v. =' is good! ';
Copy the code

Kill foreach: Many array-like functions, such as array_map, array_walk, and so on, need to be handled using closures. In the example above, we use array_walk to process the contents of an array. Is it very functional programming, and very clear.

1function testH()
3    return function ($name4) {echo "this is " . $name; 5}; 6} 7testH()("testH's closure!"); // this is testH's closure!
Copy the code

Don’t get confused when you see this code. PHP7 supports the immediate execution syntax, which is the IIFE(Immediately- Invoked function expression) in JavaScript.

Here’s another one to calculate the Fibonacci sequence:

1$fib = function ($n) use (&$fib2) {if ($n= = 0 | |$n == 1) {
3        return1; 4} 5 6return $fib($n - 1) + $fib($n- 2); 7}; 8 and 9echo $fib(10);
Copy the code

Again, recursively. Instead, I’m using a recursive implementation of closures. Finally, it is important to note that the variable name passed in use cannot be an array item with an object:

1$fruits = ['apples'.'oranges'];
2$example = function () use ($fruits[0]) { // Parse error: syntax error, unexpected '[', expecting ', ' or ') '
3    echo $fruits[0]; 
5$example(a);Copy the code

Writing like this is directly a syntax error and will not run successfully.


Closure capabilities are heavily used in the IoC service container in Laravel, and we simulate one for you to understand. A better solution, of course, is to look through Laravel’s source code yourself.

 1class B
 3class C
 5class D
 7class Ioc
 9    public $objs = [];
10    public $containers = [];
12    public function __construct()
13    {
14        $this->objs['b'] = function() {15returnnew B(); 16}; 17$this->objs['c'] = function() {18returnnew C(); 19}; 20$this->objs['d'] = function() {21returnnew D(); 22}; 23 } 24 publicfunction bind($name) 25 {26if(! isset($this->containers[$name]) {27if (isset($this->objs[$name]) {28$this->containers[$name] = $this->objs[$name]();
29            } else{30returnnull; 31} 32} 33return $this->containers[$name]; 34} 35} 36 37$ioc = new Ioc();
38$bClass = $ioc->bind('b');
39$cClass = $ioc->bind('c');
40$dClass = $ioc->bind('d');
41$eClass = $ioc->bind('e');
43var_dump($bClass); // B
44var_dump($cClass); // C
45var_dump($dClass); // D
46var_dump($eClass); // NULL
Copy the code


Closure features often appear in the functionality of event callback classes, as well as in implementations of IoC like egg Eggs. Closures have a powerful ability to lazily load. In the IoC example, our closure returns an object that comes out of new. If $ioc->bind(‘b’) is not called when the program is running, then the b object will not be created. We use closures to actually create objects when we need them out of the service container. Similarly, event callbacks are the same concept. When the event occurs, we execute the code in the callback when we need to handle it. If there were no concept of closures, then the $objs container would write:

1$this->objs['b'] = new B();
2$this->objs['c'] = new C();
3$this->objs['d'] = new D();
Copy the code

All classes must be instantiated when the container instantiates them. In this way, a lot of unnecessary objects are created for the program, resulting in a huge waste of resources.

Based on the power of closures, closure functions are now ubiquitous in Laravel, TP6, and other frameworks. There is no end to learning, and it is often more efficient to learn the framework after mastering the principle.

Test code: https://github.com/zhangyue0503/dev-blog/blob/master/php/201911/source/%E8%BF%98%E4%B8%8D%E7%9F%A5%E9%81%93PHP%E6%9C%89 %E9%97%AD%E5%8C%85%EF%BC%9F%E9%82%A3%E4%BD%A0%E7%9C%9FOUT%E4%BA%86.php

Reference documents: https://www.php.net/manual/zh/functions.anonymous.php https://www.php.net/manual/zh/functions.anonymous.php#100545 https://www.php.net/manual/zh/functions.anonymous.php#119388