📜 ⬆️ ⬇️

Php closures

It's no secret that PHP 5.3 introduced a number of interesting innovations. Different degrees of utility and scandal. It is even possible that the release of PHP 5.3 is a well-planned PR move: the largest list of changes in the last five years, the goto (sic!) Operator, namespaces ( namespaces ) with the “not like everyone else” syntax, late static binding ( late static binding ), more or less honest anonymous (lambda) functions ( lambda functions ), closures ( closures ).

About the last I want to tell. To be fair, I want to add that in PHP 5.3 a lot of other functionality was introduced that makes this release remarkable: modules Intl (internationalization), Phar (PHP archives, like JAR for JAVA), mysqlnd (new driver for mysql), improvements in SPL , overall performance increase up to 10% and a lot more then.

Closures and lambda functions - innovations in PHP 5.3, generally speaking, are not the advanced frontier of modern technology. Surprisingly, both appeared in the functional programming language LISP in the late 50s, and were doped with a file to a state close to the modern in the 70s. Thus, PHP has lagged behind with a competent implementation of 40 years.
Closures , according to Wikipedia , are procedures that refer to free variables in their lexical context. Academically strictly, but if I did not know what it was about, I would never have understood from this definition.

In many programming languages, a function whose definition is nested in another function, one way or another, can have access not only to its local variables, but also to variables of the parent function. Sometimes some special syntax is used for this, sometimes it works without extra effort. I will give an example of Javascript , because everything works there without some special syntax (the same examples, but in PHP will be later):

//***  1j *** function outer(x) //   { var y=2; //    function inner(a) //   { var b=4; //    /*       *  ,      *    */ var res=x+y+a+b; alert(res); // 10   . } inner(3); //   } outer(1); //  ,    . 

So, the inner function, without any special declaration, freely uses the variables of the outer outer function. But this is not a closure .

In many programming languages, a function (meaning not the result of executing a function, but the function itself as executable code, a pointer to a function) is a certain object of a special type. Like any other object (string, number, array), a function can be assigned to a variable, passed to another function as a parameter, and returned as the result of the execution of another function. Another important thing is related to this: When defining a function, you can not assign a name, but assign this function to a variable and call it through this variable . The function itself does not have a name of its own, and therefore remains " anonymous ." Such functions are also called lambda functions .

 // ***  2j *** adder=function(a,b) //   ,     { return a+b; //  } subber=function(a,b) { return ab; } //  ,     /* *    -   adder  subber   *        , ,  , *   . ..   adder  subber   ,  , *    : x=adder(1,3);   x   . */ function performAction(action, a, b) //    performAction { var result=action(a,b); //   action -   return result; } function makeDivider() //    makeDivider { return function (a,b) //   { return a/b; } } r1=adder(1,2); //    . r1=1+2; r2=performAction(subber,6,4); //    . r2=6-4; r3=performAction(function(a,b) {return a*b;} ,5,6); //     . r3=5*6; divider=makeDivider(); // ,   ,   r4=divider(16,4); //     : r4=16/4; r5=makeDivider()(32,16);//  ,    : r5=32/16; alert([r1,r2,r3,r4,r5]); //3,2,30,4,2 

If the previous examples are more or less clear, then the closures will also be clear, because they are, in fact, a combination of the two principles described above:

Those. nested function as it closes the variables from the parent osprey, not allowing them to be destroyed . And here is a useful Javascript example:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Closure test</title> </head> <body> <a href="#" id="link1"></a> <a href="#" id="link2">  </a> <div id="hide1">  </div> <div id="hide2">   </div> <script> // ***  3j *** function getHider(id) // id     { return function() { document.getElementById(id).style.display='none'; return false; } /* *     ,   id  *            . *  getHider    *          , *       . */ } document.getElementById('link1').onclick=getHider('hide1'); document.getElementById('link2').onclick=getHider('hide2'); /* *      onclick    *    getHider,    ,   *  ,    id='hide1'    *  id='hide2'  .  ,   *    ,      */ </script> </body> </html> 

Lambda functions (and closures, being them) as though postpone execution of some code for later term. Therefore, they are often used in the development of user interfaces as event handlers, as was shown above.

Let's go back to PHP . Everything in it is not as beautiful as in Javascript. First, in order for the function to use the variables of the parent function, it is necessary to explicitly indicate this in its definition (this follows from the ideology of the language), second, it only works with anonymous functions and it only works with PHP 5.3 . Example 1j performed by PHP will look something like this:

 // ***  1p *** function outer($x) //   { $y=2; //    $inner=function ($a) use ($x, $y) //   { $b=4; //    /*       *  ,      *    */ $res=$x+$y+$a+$b; echo $res; // 10   . }; $inner(3); //   } outer(1); 

And our example 2j something like this:

 // ***  2p *** $adder=function($a,$b) //   ,     { return $a+$b; //  }; $subber=function($a,$b) { return $a-$b; }; //  ,     function performAction($action, $a, $b) //    performAction { $result=$action($a,$b); //   action -   return $result; } function makeDivider() //    makeDivider { return function ($a,$b) //   { return $a/$b; }; } $r1=$adder(1,2); //    . r1=1+2; $r2=performAction($subber,6,4); //    . r2=6-4; $r3=performAction(function($a,$b) {return $a*$b;} ,5,6); //     . r3=5*6; $divider=makeDivider(); // ,   ,   $r4=$divider(16,4); //     : r4=16/4; //     r5  PHP   . //$r5=makeDivider()(32,16);//  ,    : r5=32/16; $r5='php fail'; echo "$r1,$r2,$r3,$r4,$r5"; //3,2,30,4,php fail 

Anonymous functions in PHP allow you to simplify and make clearer the use of various built-in functions using callback , for example:

 // : function cmp($a, $b) { return($a > $b); } //    uasort($array, 'cmp'); //     // : uasort($array, function($a, $b) { return($a > $b);}); 

In the old version, the code of the comparison function and the code where it is used may be placed quite far apart, moreover, this function is used only once, but it must be given a name and it may accidentally come into conflict with the name of another function. In the new version, everything is much nicer, all the code is placed compactly and the namespace also does not clog up.

It may seem that anonymous functions were already in previous versions of PHP. Is create_function , is it not the same thing? Yes and no. create_function on every call creates a new real named function in the global namespace (sic!), but its name starts with \ 0 (zero byte) and therefore cannot conflict with normal functions. But is it really possible to call such a function anonymous? By the way, create_function returns a string with the name of this “anonymous” function. Otherwise, the use is really similar to the use of anonymous functions in PHP 5.3:

 //  ,    PHP 4!: uasort($array, create_function('$a, $b','return $a > $b;')); 

Why do I need closures in PHP, I understand poorly. To simply save the state of a function between calls? For example:

 function getModernIncrementer() { $x=0; return function() use(&$x) //    ! { return $x++; }; } $incrementer2=getModernIncrementer(); echo $incrementer2(), $incrementer2(), $incrementer2();//012 

But for this there are static variables. Their use is simpler, more convenient and requires less code:

 function incrementer() { static $x=0; return $x++; } echo incrementer(),incrementer(),incrementer(); //012 

For complex state preservation between calls, there is an OOP . By the way, anonymous functions in PHP 5.3 are not exactly honest functions! In practice, this function turns out to be ... turns out ... Attention focus:

 var_dump(function(){return 1;}); // object(Closure)#1 (0) { } 

The function is an object of the Closure class - and here it is your OOP. And so that the object in every possible way posed a function out of itself, the PHP developers invented a special “magic” method __invoke () :

 class Test { public function __invoke () { return 123; } } $func = new Test(); echo $func(); //123 

To summarize: Anonymous functions and closures are a very powerful tool. And like any powerful tool, they require very careful application. They can significantly simplify the program code, make it more beautiful and improve its readability, but they can do exactly the opposite - make it completely unreadable. Closures are very often used as callback functions when programming GUIs. It is very convenient to hang them as handlers for clicking on different buttons. But in PHP, almost no one makes programs with GUIs ( although there are craftsmen ), and there are some reasons for this - PHP is still a web scripting language, not desktop applications. Therefore, anonymous functions are good in preg_replace_callback , array_filter, and similar functions, and closures should be left for Javascript , Python, and other languages, where they are implemented really well and where they are really needed for use in the GUI.

In conclusion: I want to give a complete example of using closures with jQuery (when working with this library, they are widely used). In the example, there are many nested functions, handlers, and in order to avoid constant (albeit fast, but ugly) searches for DOM objects by parameters, variables already containing the desired object, derived from the external function, are used everywhere (this is the closure). The same code is used in the admin of the hi-tech.mail.ru project, and allows you to dynamically add the ability to edit and save individual blocks of the page via AJAX (like what the Editable plugin does), while the HTML code of the page does not contain any special markup for this .

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>dynaedit</title> <style type="text/css"> .msg, .edt {font-family: "Verdana", "Arial", "Helvetica", sans-serif; font-size: 10pt} .msg {background-color:#DDf; border: 1px solid #000; padding:2px} .edt {margin:-2px 0 -2px -1px; padding:0; width:100%; height:100%; border 0} </style> <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script> <script> $(document).ready(function() { $("#admin").click(function() { $(this).remove(); $("p.msg").each(function() { var msg_id=this.id.substr(1); //  this?  each! var msg=$(this); // JQuery  DOM  <p> $("<input/>", //    { type: "button", value: "  #"+msg_id, click: function StartEdit() //      p   textarea   { var edt=$("<textarea/>", // textarea,     p { 'class':'edt', value:msg.html().replace(/<br[^>]*>/gim,"\n"), // msg?    =>  height:msg.height(), }).appendTo(msg.empty()); $(this).val("  #"+msg_id).unbind().click(function() //      { //$.post("/ajax/savemessage",{msg_id:msg_id, msg:edt.val()}, function(data){}); //   msg.html(edt.remove().val().replace(/\n/gm,"<br />")); // textarea,   $(this).val("  #"+msg_id).unbind().click(StartEdit);// ,      return false; });//Save return false; } //StartEdit() }).insertAfter(this);//<input/> });//$("p.msg").each return false; });//$("#admin").click });//$(document).ready </script> </head> <body> <p id="p1234" class="msg">  <br />  !</p> <p id="p1235" class="msg">  <br />   !<br />PS Just 4 lulz</p> <p><a href="#" id="admin">    !</a></p> </body> </html> 

Mail.Ru Research and Development Department

Source: https://habr.com/ru/post/103983/

All Articles