An explanation of anonymous functions and closures in PHP

Source: Internet
Author: User
Tags vars
anonymous functions in theProgramming languages Appeared early in the Lisp language, and then a lot of programming languages began to have this function,

Currently using a wide range of JavaScript and c#,php until 5.3 to really support anonymous functions, the new standard for C + + c++0x also began to support.

An anonymous function is a class of functions or subroutines that do not require a specified identifier and can be called, and the anonymous function can be easily passed as a parameter to other functions, the most common being a callback function.

Closures (Closure)

Speaking of anonymous functions, we have to mention closures, closures are lexical closures (Lexical Closure) abbreviation, is a reference to the function of the free variable, the applied free variable will exist with this function, even if left to create its environment is also the same, So closures can also be thought of as entities that have functions and combinations of their associated references. In some languages, when defining another function within a function, a closure may be generated if an intrinsic function references a variable of an external function. When you run an external function, a closure is formed.

This word and anonymous functions can easily be mixed, in fact, this is two different concepts, this may be because many languages implement anonymous functions when the formation of closures.

Using Create_function () to create an "anonymous" function

Before the formal support for anonymous functions is mentioned in PHP5.3, there may be some attentive readers here, because there is a function that can generate anonymous functions: The Create_function function, This function can be found in the manual in PHP4.1 and PHP5, which can also be used as an anonymous callback function, such as the following:

<?php $array = Array (1, 2, 3, 4); Array_walk ($array, create_function (' $value ', ' echo $value '));

This code simply outputs the values in the array sequentially, and can do more of course. So why is this not really an anonymous function, let's look at the return value of this function, this function returns a string, usually we can call a function like this:

<?php function A () {    echo ' function a ';} $a = ' a '; $a ();

We can also implement the callback function in this way, for example:

<?php function do_something ($callback) {    //doing    # ...     Done    $callback ();}

This enables you to invoke the function specified by $callback after the function do_something () execution completes. Back to the return value of the Create_function function: The function returns a unique string function name, and False if an error occurs. So this function is just a dynamic creation of a function, which is the function of the name , that is to say, this is not anonymous. It just creates a globally unique function.

<?php$func = Create_function (', ' echo ' function created dynamic '; '); Echo $func; Lambda_1 $func ();    Function created dynamic $my _func = ' lambda_1 '; $my _func (); This function does not exist lambda_1 (); This function does not exist

The above code is very well understood before, create_function is so used, after the function name to call but failed, which is a bit difficult to understand, how does PHP ensure that the function is globally unique? Lambda_1 seems to be a very common function name, what if we first define a function called Lambda_1? The return string of the function here is lambda_2, which, when the function is created, checks to see if the function exists. Know what the function name is. But what if we define a function called Lambda_1 after create_function? So there is the problem of duplicate function definition, this implementation is probably not the best way, in fact, if you really define a function named Lambda_1 will not appear I said the problem. What the hell is going on here? The last 22 lines of the above code also illustrate the problem, and there is actually no definition of a function named Lambda_1.

That is to say our lambda_1 and create_function return lambda_1 is not the same!? How could that be? That only means that we don't see the substance, we see only the surface, the surface is when we output the lambda_1 in Echo, and our lambda_1 is our own. Let's take a look at it using the Debug_zval_dump function.

<?php$func = Create_function (', ' echo ' Hello '; '); $my _func_name = ' lambda_1 ';d ebug_zval_dump ($func);         String (9) "Lambda_1" RefCount (2) debug_zval_dump ($my _func_name); String (8) "Lambda_1" RefCount (2)

Look out, their lengths are not the same, the length is not the same as the same function, so we call the function of course does not exist, we still directly see what the Create_function function has done. This realization is shown in: $PHP _SRC/ZEND/ZEND_BUILTIN_FUNCTIONS.C

#define LAMBDA_TEMP_FUNCNAME    "Lambda_func" Zend_function (create_function) {    //... Omit extraneous code    function_name = (char *) emalloc (sizeof ("0lambda_") +max_length_of_long);    Function_name[0] = ' + ';  <---Here do    {        function_name_length = 1 + sprintf (function_name + 1, "lambda_%d", ++eg (Lambda_count));    } W Hile (Zend_hash_add (EG (function_table), Function_name, function_name_length+1, &new_function, sizeof (zend_ function), NULL) ==failure);    Zend_hash_del (EG (function_table), Lambda_temp_funcname, sizeof (Lambda_temp_funcname));    Return_stringl (function_name, function_name_length, 0);}

After defining a function, the function gives a name to the function, which changes the first character of the function name to ' + ', which is the null character, and then looks in the function table to see if the function has already been defined, and if it does, generates a new function name, and the first character is a null character that is defined in a special way. Because it is not possible to define such a function in user code, there is no problem of naming conflicts, which is also a kind of trickery (tricky) practice, after the understanding of this special function, we can actually call to this function, as long as we add a null character in front of the function name, Chr () function can help us generate such a string, such as the previously created function can be accessed by the following way:

<?php $my _func = chr (0). "Lambda_1"; $my _func (); Hello

There are some drawbacks to this way of creating an "anonymous function":

    1. The function is defined by the dynamic eval of the string, which makes it impossible to check the basic syntax;

    2. There is no essential difference between such functions and ordinary functions, and the effect of closures cannot be achieved.

The Real anonymous function

Among the many features introduced by PHP5.3, there is a feature in addition to anonymous functions worth talking about: The new Invoke Magic method introduced.

Invoke Magic Method

The time for this magical method to be invoked is when an object is called as a function, and if the object defines the Invoke Magic method, the function is called, which is somewhat similar to the operator overloading in C + +, for example, as follows:

<?phpclass Callme {public    function invoke ($phone _num) {        echo "Hello: $phone _num";    }} $call = new Callme () ; $call (13810688888); "hello:13810688888

Implementation of anonymous functions

The method of invoking an object as a function is described earlier, and you might think of the method of implementing an anonymous function in PHP, and the anonymous function in PHP is actually implemented in this way. Let's check it out first:

<?php$func = function () {    echo "Hello, anonymous function";} Echo GetType ($func);    Objectecho Get_class ($func);  Closure

The original anonymous function is just an ordinary class. Familiarity with JavaScript is familiar with the use of anonymous functions, PHP is also used with JavaScript-like syntax to define, anonymous functions can be assigned to a variable, because the anonymous function is actually a class instance, so can copy is very easy to understand, In JavaScript, you can assign an anonymous function to a property of an object, for example:

var a = {};a.call = function () {alert ("called");} A.call (); Alert called

This is common in JavaScript, but in PHP it is not possible to copy the properties of the object can not be called, so that the use will cause the class to look for methods defined in the class, in PHP, the property name and the definition of the method name can be duplicated, which is determined by the PHP class model, Of course, PHP in this area can be improved, the subsequent version may allow such a call, so it is easier to implement some of the flexibility of the function. There is also a way to achieve this effect: using another Magic method call (), as for how to implement it is left to the reader as a problem.

Use of closures

PHP uses closures (Closure) to implement anonymous functions, the most powerful function of the anonymous function is also in the anonymous function provides some of the dynamic characteristics and closure effect, the anonymous function at the time of definition if you need to use the domain-scoped variables need to use the following syntax to implement:

<?php$name = ' TIPI Team '; $func = function () use ($name) {    echo "Hello, $name";} $func ();//Hello TIPI Team

This use statement looks awkward, especially compared to JavaScript, but this should also be the syntax used by Php-core, because unlike JavaScript, PHP defines variables by default as local variables. In JavaScript, in contrast, in addition to the explicit definition of the local variable, PHP in the mutation can not determine whether the variable is a local variable or a variable in the upper scope, of course, there may be a way to determine at compile time, but this has a great impact on the efficiency and complexity of the language.

This syntax is more straightforward, if you need to access the upper scope of the variables need to use a statement to declare, this is also easy to read, in this case, you can actually use to achieve similar effects of the global statement.

Anonymous functions have access to variables in the upper scope each time they are executed, and these variables always hold their own state before the anonymous function is destroyed, such as the following example:

<?phpfunction Getcounter () {    $i = 0;    return function () use ($i) {//here if the reference passed in variable is used: using (& $i)        echo + + $i;    };} $counter = Getcounter (); $counter ();//1$ Counter (); 1

Unlike JavaScript, where the two function calls do not make the $i variable self-increment, the default PHP is copied to the upper-level variable into the anonymous function, if the need to change the value of the upper variable will need to be passed by reference. So the code above has no output 1, 2 but 1,1 .

Implementation of closures

As mentioned earlier, anonymous functions are implemented by closures, and now we begin to see how closures (classes) are implemented. There is no difference between an anonymous function and a normal function except for a variable name, and the implementation code for the closure is $php_src/zend/zend_closure.c. The question of the "object" of the anonymous function has been implemented through closure, and how does anonymous access the variable when the anonymous function was created?

For example, the following code:

<?php$i=100; $counter = function () use ($i) {    debug_zval_dump ($i);};   $counter ();

Look through VLD to see what kind of opcode this code compiles.

$ php-dvld.active=1 closure.php vars:!0 = $i,! 1 = $counter # * OP FETCH ext return                                                   Operands------------------------------------------------------------------------0 > ASSIGN !0, 1001 zend_declare_lambda_function '%00%7bclosure2                                       IGN! 1, ~13 Init_fcall_by_name                                                   !14 do_fcall_by_name 0 5 > RETURN                           1 Function Name: {Closure}number of ops:5compiled vars:!0 = $iline # * OP     FETCH ext return operands--------------------------------------------------------------------------------3                                                   0 > Fetch_r static $ ' i ' 1 ASSIGN !0, $4 2 send_var!0 3 Do_fcall 1 ' debug_zval_dump ' 5 4 > RETURN n Ull

The above based on the situation to remove some unrelated output, from top to bottom, the 1th 100 is assigned to!0 that is the variable $i, then execute zend_declare_lambda_function, then we go to the relevant opcode execution function to see how this is done, This opcode handler function is located in the $php_src/zend/zend_vm_execute.h:

static int Zend_fastcall  Zend_declare_lambda_function_spec_const_const_handler (Zend_opcode_handler_args) {    zend_op *opline = EX (opline);    Zend_function *op_array;     if (Zend_hash_quick_find (EG (function_table), Z_strval (opline->op1.u.constant), Z_strlen (opline-> Op1.u.constant), Z_lval (opline->op2.u.constant), (void *) &op_array) = = FAILURE | |        Op_array->type! = zend_user_function) {        Zend_error_noreturn (e_error, "Base Lambda FUNCTION for closure not found" );    }     Zend_create_closure (&ex_t (Opline->result.u.var) Tmp_var, Op_array tsrmls_cc);     Zend_vm_next_opcode ();}

The function calls the Zend_create_closure () function to create a closure object, so let's continue to look at what the Zend_create_closure () function in $PHP_SRC/ZEND/ZEND_CLOSURES.C has done.

Zend_api void Zend_create_closure (Zval *res, zend_function *func tsrmls_dc) {zend_closure *closure;     OBJECT_INIT_EX (res, zend_ce_closure);     Closure = (Zend_closure *) Zend_object_store_get_object (res tsrmls_cc);     Closure->func = *func; if (Closure->func.type = = zend_user_function) {//If the user-defined anonymous function if (closure->func.op_array.static_variables)             {HashTable *static_variables = closure->func.op_array.static_variables;             Request a hash table space alloc_hashtable (closure->func.op_array.static_variables) for a function to store static variables; Zend_hash_init (Closure->func.op_array.static_variables, Zend_hash_num_elements (static_variables), NULL, ZVAL_             Ptr_dtor, 0); Loop the current list of static variables, using the Zval_copy_static_var method to process zend_hash_apply_with_arguments (Static_variables tsrmls_cc, Apply_fun        c_args_t) Zval_copy_static_var, 1, closure->func.op_array.static_variables);    } (*closure->func.op_array.refcount) + +; } closure->funC.common.scope = NULL;} 

As mentioned in the code comment above, continue to see the implementation of the Zval_copy_static_var () function:

static int Zval_copy_static_var (Zval **p tsrmls_dc, int num_args, va_list args, Zend_hash_key *key) {HashTable *target    = Va_arg (args, hashtable*);     Zend_bool Is_ref;        Only static variables with the USE statement type are evaluated, otherwise static variables in the body of the anonymous function also affect variables outside the scope if (Z_TYPE_PP (P) & (Is_lexical_var|is_lexical_ref)) {         Is_ref = Z_TYPE_PP (p) & Is_lexical_ref; if (!        EG (active_symbol_table)) {zend_rebuild_symbol_table (Tsrmls_c); }//If there is no such variable within the current scope if (Zend_hash_quick_find (EG (active_symbol_table), Key->arkey, Key->nkeylength, key                 ->h, (void *) &p) = = FAILURE) {if (is_ref) {zval *tmp;                If it is a reference variable, create a temporary variable to manipulate the variable after the anonymous function definition alloc_init_zval (TMP);                z_set_isref_p (TMP); Zend_hash_quick_add (EG (active_symbol_table), Key->arkey, Key->nkeylength, key->h, &tmp, sizeof (zval*),            (void**) &p); } else {//If not a reference means that the variable does not exist P =&eg (UNINITIALIZED_ZVAL_PTR);            Zend_error (E_notice, "Undefined variable:%s", Key->arkey); }} else {///If there is a variable, refer to the variable or copy if (is_ref) {separate_zval_to_m), depending on whether it is a reference            Ake_is_ref (P);            } else if (Z_ISREF_PP (p)) {separate_zval (P); }}} if (Zend_hash_quick_add (target, Key->arkey, Key->nkeylength, Key->h, p, sizeof (zval*), NULL) =    = SUCCESS) {z_addref_pp (P); } return zend_hash_apply_keep;}

This function is passed as a callback function to the zend_hash_apply_with_arguments () function, which is processed by this function each time it is read to the value in the hash table. This function assigns the variable value defined by all use statements to the static variable of the anonymous function, so that the anonymous function can access the use's variable.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.