Hi Christian,

I'm fine with your suggestion for lexical variables syntax, but I don't
know if we really need brackets around them. For now I changed syntax in
the following way.

$func = function ($x, $y) use $a, $b, $c {
}

According to segfault, I added a check that emits fatal error.

I don't like to use separate HashTable for lexical variables, because
- it takes memory (however it won't be used for all regular op_arrays)
- it requires new special modifier for FETCH opcode
- opcode caches must check for this additional table and copy it

My idea with usage of static_variables doesn't require any opcode cache
modification at all.

I'm fine if you'll improve my patch (It's mainly yours :)

Thanks. Dmitry.


Christian Seiler wrote:
> Hi Dmitry,
> 
> First of all: Your patch does really simplify things internally quite a
> bit - I like it. I have a few issues though:
> 
>> The patch shouldn't affect opcode caches and other extensions as it
>> doesn't change any structures.
> 
> I don't see a problem in changing structures for either extensions nor
> opcode caches - as long as only entries are added. Binary compability
> with PHP 5.2 is not provided anyway (by neither 5.3 nor 6) and source
> compability is not affected if the old members are not touched or their
> semantics change.
> 
>> It uses the op_array->static_variables for lexical variables.
> 
> That's a point I don't like. Although you use IS_CONSTANT to cleverly
> mask lexical variables, I really think a separate hash table would be a
> far better idea, especially for code maintainability.
> 
>> The patch also fixes several small issues and adds some missing
>> functionality which didn't allow preg_replace_callback() (and may be
>> others) to work with lambda functions.
> 
> Oh yes, I somehow missed that, thanks!
> 
>> Please review.
> 
> I (personally) have some smaller issues with the patch and one big
> issue:
> 
> Smaller issues:
> 
>  * A separate hash table for the lexical variables would be much cleaner
>    in my eyes.
> 
>  * The segfault that occurs with my patch still occurs with yours (see
>    below for an example)
> 
> But the one big issue is the syntax: ($foo | $bar) is just extremely
> painful in my eyes. I wouldn't want to use it - and it would be quite
> confusing (which side are the normal parameters, which side are the
> lexical vars?). I do see your point that the 'lexical' keyword inside
> the function body to actually have an effect on the function semantics
> is not optimal and that the list of lexical variables is probably better
> placed in the function definition. I therefore propose the following
> syntax:
> 
> function (parameters) { }                   // no closure, simply lambda
> function (parameters) KEYWORD (lexical) { } // closure with lexical vars
> 
> KEYWORD could be for example 'use'. That probably describes best what
> the function does: Use/import those variables from the current scope.
> Example:
> 
> return function ($x) use ($s) {
>   static $n = 0;
>   $n++;
>   $s = $n.':'.$s;
>   $this->foo($x[0].':'.$s);
> };
> 
> As for simply omitting the keyword, e.g. function () () - as already
> suggested: I don't like that syntax either. Although I'm not a fan of
> too much language verbosity (that's why I don't like Fortran, Basic and
> Pascal), I think in this case, a little more verbosity wouldn't hurt -
> and typing 'use' is just 3 additional characters.
> 
> Now for the examples for the smaller issues:
> 
> Segfault:
> 
> <?php
> 
> $a = function () {
>   $GLOBALS['a'] = NULL;
>   echo "destroyed closure\n";
> };
> 
> var_dump ($a);
> $a ();
> ?>
> 
> This crashes - due to the fact that the currently used op_array is
> destroyed upon destruction of the variable. This could get even more
> interesting if the closure called itself recursively. My proposal is to
> create a copy (but not a reference, just do a normal copy, for resources
> or objects that will just do the trick) of the variable internally in
> zend_call_function and zend_do_fcall_common_helper into a dummy zval and
> destroy that zval after the function call ended. That way, the GC won't
> kick in until after the execution of the closure. In zend_call_function
> that's easy - in zend_do_fcall_common helper we have the problem that
> the variable containing the closure is no longer available. An idea
> could be that the INIT_FCALL functions always additionally push the
> lambda zval to the argument stack (inside the function it will be
> ignored) and the fcall_common_helper will remove that zval from the
> stack prior to returning (and free it). If a non-closure is called, NULL
> (or an empty zval or whatever) could be pushed to the stack instead.
> Hmm, perhap's I'll have a better idea tomorrow.
> 
> Anyway, since Andi suggested to use objects instead of resources, I'd
> like to use your patch as a starting point, if there are no objections.
> 
> Regards,
> Christian
Index: Zend/zend_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_API.c,v
retrieving revision 1.296.2.27.2.34.2.38
diff -u -p -d -r1.296.2.27.2.34.2.38 zend_API.c
--- Zend/zend_API.c     5 Jun 2008 18:53:06 -0000       1.296.2.27.2.34.2.38
+++ Zend/zend_API.c     20 Jun 2008 10:57:09 -0000
@@ -2615,6 +2615,28 @@ ZEND_API zend_bool zend_is_callable_ex(z
                        }
                        return 0;
 
+               case IS_RESOURCE:
+                       {
+                               zend_closure *closure = (zend_closure*) 
zend_fetch_resource(&callable TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func);
+
+                               if (closure) {
+                                       *fptr_ptr = 
(zend_function*)&closure->op_array;
+                                       if (closure->This) {
+                                               *zobj_ptr_ptr = &closure->This;
+                                               *ce_ptr = 
Z_OBJCE_P(closure->This);
+                                       } else {
+                                               *zobj_ptr_ptr = NULL;
+                                               *ce_ptr = 
closure->op_array.scope;
+                                       }
+                                       if (callable_name) {
+                                               *callable_name_len = 
strlen(closure->op_array.function_name);
+                                               *callable_name = 
estrndup(closure->op_array.function_name, *callable_name_len);
+                                       }                                       
                                
+                                       return 1;
+                               }
+                       }
+                       /* break missing intentionally */
+
                default:
                        if (callable_name) {
                                zval expr_copy;
Index: Zend/zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.647.2.27.2.41.2.68
diff -u -p -d -r1.647.2.27.2.41.2.68 zend_compile.c
--- Zend/zend_compile.c 15 Jun 2008 18:27:37 -0000      1.647.2.27.2.41.2.68
+++ Zend/zend_compile.c 20 Jun 2008 10:57:09 -0000
@@ -1375,6 +1375,29 @@ void zend_do_begin_function_declaration(
        CG(labels) = NULL;
 }
 
+void zend_do_begin_lambda_function_declaration(znode *result, znode 
*function_token, int return_reference TSRMLS_DC)
+{
+       znode          function_name;
+       zend_op_array *current_op_array = CG(active_op_array);
+       int            current_op_number = 
get_next_op_number(CG(active_op_array));
+       zend_op       *current_op;
+
+       function_name.op_type = IS_CONST;
+       ZVAL_STRINGL(&function_name.u.constant, "lambda", sizeof("lambda")-1, 
1);
+
+       zend_do_begin_function_declaration(function_token, &function_name, 0, 
return_reference, NULL TSRMLS_CC);
+
+       result->op_type = IS_TMP_VAR;
+       result->u.var = get_temporary_variable(current_op_array);;
+
+       current_op = &current_op_array->opcodes[current_op_number];
+       current_op->opcode = ZEND_DECLARE_LAMBDA_FUNCTION;
+       zval_dtor(&current_op->op2.u.constant);
+       ZVAL_LONG(&current_op->op2.u.constant, 
zend_hash_func(Z_STRVAL(current_op->op1.u.constant), 
Z_STRLEN(current_op->op1.u.constant)));
+       current_op->result = *result;
+}
+
+
 void zend_do_handle_exception(TSRMLS_D)
 {
        zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
@@ -4149,6 +4172,21 @@ void zend_do_fetch_static_variable(znode
 /*     zval_dtor(&varname->u.constant); */
 }
 
+void zend_do_fetch_lexical_variable(znode *varname TSRMLS_DC)
+{
+       znode value;
+
+       /* Empty constant has a special meaning of lexical variable.
+        * It is substituted by real value in ZEND_DECLARE_LAMBDA_FUNCTION */
+       value.op_type = IS_CONST;
+       ZVAL_EMPTY_STRING(&value.u.constant);
+       Z_TYPE(value.u.constant) = IS_CONSTANT;
+       Z_SET_REFCOUNT_P(&value.u.constant, 1);
+       Z_UNSET_ISREF_P(&value.u.constant);
+       
+       zend_do_fetch_static_variable(varname, &value, ZEND_FETCH_STATIC 
TSRMLS_CC);
+}
+
 void zend_do_fetch_global_variable(znode *varname, znode *static_assignment, 
int fetch_type TSRMLS_DC)
 {
        zend_op *opline;
Index: Zend/zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.316.2.8.2.12.2.25
diff -u -p -d -r1.316.2.8.2.12.2.25 zend_compile.h
--- Zend/zend_compile.h 11 Jun 2008 13:18:39 -0000      1.316.2.8.2.12.2.25
+++ Zend/zend_compile.h 20 Jun 2008 10:57:09 -0000
@@ -429,6 +429,14 @@ void zend_do_end_function_call(znode *fu
 void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
 void zend_do_handle_exception(TSRMLS_D);
 
+typedef struct _zend_closure {
+       zend_op_array     op_array;
+       zval             *This;
+} zend_closure;
+
+void zend_do_begin_lambda_function_declaration(znode *result, znode 
*function_token, int return_reference TSRMLS_DC);
+void zend_do_fetch_lexical_variable(znode *varname TSRMLS_DC);
+
 void zend_do_try(znode *try_token TSRMLS_DC);
 void zend_do_begin_catch(znode *try_token, znode *catch_class, znode 
*catch_var, znode *first_catch TSRMLS_DC);
 void zend_do_end_catch(znode *try_token TSRMLS_DC);
Index: Zend/zend_execute.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute.c,v
retrieving revision 1.716.2.12.2.24.2.30
diff -u -p -d -r1.716.2.12.2.24.2.30 zend_execute.c
--- Zend/zend_execute.c 11 Jun 2008 13:18:39 -0000      1.716.2.12.2.24.2.30
+++ Zend/zend_execute.c 20 Jun 2008 10:57:09 -0000
@@ -1259,6 +1259,27 @@ static inline zend_brk_cont_element* zen
        return jmp_to;
 }
 
+static int zval_copy_static_var(zval **p, int num_args, va_list args, 
zend_hash_key *key)
+{
+       HashTable *target = va_arg(args, HashTable*);
+
+       if (Z_TYPE_PP(p) == IS_CONSTANT && Z_STRLEN_PP(p) == 0) {
+               TSRMLS_FETCH();
+
+               if (!EG(active_symbol_table)) {
+                       zend_rebuild_symbol_table(TSRMLS_C);
+               }
+               if (zend_hash_quick_find(EG(active_symbol_table), key->arKey, 
key->nKeyLength, key->h, (void **) &p) == FAILURE) {
+                       return ZEND_HASH_APPLY_KEEP;
+               }
+               SEPARATE_ZVAL_TO_MAKE_IS_REF(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;
+}
+
 #if ZEND_INTENSIVE_DEBUGGING
 
 #define CHECK_SYMBOL_TABLES()                                                  
                                                        \
Index: Zend/zend_execute_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute_API.c,v
retrieving revision 1.331.2.20.2.24.2.42
diff -u -p -d -r1.331.2.20.2.24.2.42 zend_execute_API.c
--- Zend/zend_execute_API.c     5 Jun 2008 18:50:29 -0000       
1.331.2.20.2.24.2.42
+++ Zend/zend_execute_API.c     20 Jun 2008 10:57:09 -0000
@@ -739,6 +739,21 @@ int zend_call_function(zend_fcall_info *
                        SEPARATE_ZVAL_IF_NOT_REF(tmp_object_ptr);
                        fci->object_pp = tmp_object_ptr;
                        Z_SET_ISREF_PP(fci->object_pp);
+               } else if (Z_TYPE_P(fci->function_name) == IS_RESOURCE) {
+                       zend_closure *closure = (zend_closure*) 
zend_fetch_resource(&fci->function_name TSRMLS_CC, -1, NULL, NULL, 1, 
le_lambda_func);
+
+                       if (!closure) {
+                               return FAILURE;
+                       }
+                       EX(function_state).function = 
(zend_function*)&closure->op_array;
+                       if (closure->This) {
+                               fci->object_pp = &closure->This;
+                               calling_scope = Z_OBJCE_P(closure->This);
+                       } else {
+                               fci->object_pp = NULL;
+                               calling_scope = closure->op_array.scope;
+                       }
+                       goto init_fci_cache;
                }
 
                if (fci->object_pp && !*fci->object_pp) {
@@ -935,6 +950,8 @@ int zend_call_function(zend_fcall_info *
                                return FAILURE;
                        }
                }
+
+init_fci_cache:
                if (fci_cache &&
                        (EX(function_state).function->type != 
ZEND_INTERNAL_FUNCTION ||
                        
((zend_internal_function*)EX(function_state).function)->handler != 
zend_std_call_user_call)
Index: Zend/zend_language_parser.y
===================================================================
RCS file: /repository/ZendEngine2/zend_language_parser.y,v
retrieving revision 1.160.2.4.2.8.2.21
diff -u -p -d -r1.160.2.4.2.8.2.21 zend_language_parser.y
--- Zend/zend_language_parser.y 8 Jun 2008 09:38:47 -0000       
1.160.2.4.2.8.2.21
+++ Zend/zend_language_parser.y 20 Jun 2008 10:57:09 -0000
@@ -302,7 +302,7 @@ is_reference:
 
 
 unticked_function_declaration_statement:
-               T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference 
T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type, NULL 
TSRMLS_CC); }
+               function is_reference T_STRING { 
zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
                        '(' parameter_list ')' '{' inner_statement_list '}' { 
zend_do_end_function_declaration(&$1 TSRMLS_CC); }
 ;
 
@@ -510,8 +510,8 @@ class_statement_list:
 class_statement:
                variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } 
class_variable_declaration ';'
        |       class_constant_declaration ';'
-       |       method_modifiers T_FUNCTION { $2.u.opline_num = 
CG(zend_lineno); } is_reference T_STRING { 
zend_do_begin_function_declaration(&$2, &$5, 1, $4.op_type, &$1 TSRMLS_CC); } 
'('
-                       parameter_list ')' method_body { 
zend_do_abstract_method(&$5, &$1, &$10 TSRMLS_CC); 
zend_do_end_function_declaration(&$2 TSRMLS_CC); }
+       |       method_modifiers function is_reference T_STRING { 
zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); } 
'('
+                       parameter_list ')' method_body { 
zend_do_abstract_method(&$4, &$1, &$9 TSRMLS_CC); 
zend_do_end_function_declaration(&$2 TSRMLS_CC); }
 ;
 
 
@@ -643,6 +643,22 @@ expr_without_variable:
        |       T_ARRAY '(' array_pair_list ')' { $$ = $3; }
        |       '`' encaps_list '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); }
        |       T_PRINT expr  { zend_do_print(&$$, &$2 TSRMLS_CC); }
+       |       function is_reference '(' { 
zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type TSRMLS_CC); }
+                       parameter_list ')' lexical_vars '{' 
inner_statement_list '}' {  zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ 
= $4; }
+;
+
+function:
+       T_FUNCTION { $$.u.opline_num = CG(zend_lineno); }
+;
+
+lexical_vars:
+               /* emptry */
+       |       T_USE lexical_var_list
+;
+
+lexical_var_list:
+               lexical_var_list ',' T_VARIABLE         { 
zend_do_fetch_lexical_variable(&$3 TSRMLS_CC); }
+       |       T_VARIABLE                                                      
{ zend_do_fetch_lexical_variable(&$1 TSRMLS_CC); }
 ;
 
 function_call:
Index: Zend/zend_list.c
===================================================================
RCS file: /repository/ZendEngine2/zend_list.c,v
retrieving revision 1.66.2.1.2.1.2.1
diff -u -p -d -r1.66.2.1.2.1.2.1 zend_list.c
--- Zend/zend_list.c    31 Dec 2007 07:17:04 -0000      1.66.2.1.2.1.2.1
+++ Zend/zend_list.c    20 Jun 2008 10:57:09 -0000
@@ -27,6 +27,7 @@
 #include "zend_globals.h"
 
 ZEND_API int le_index_ptr;
+ZEND_API int le_lambda_func;
 
 /* true global */
 static HashTable list_destructors;
@@ -333,6 +334,26 @@ ZEND_API int zend_fetch_list_dtor_id(cha
        return 0;
 }
 
+
+static void zend_lambda_func_resource_dtor(zend_rsrc_list_entry *rsrc 
TSRMLS_DC)
+{
+       zend_closure *closure = (zend_closure *)rsrc->ptr;
+       zend_execute_data *ex = EG(current_execute_data);
+
+       while (ex) {
+               if (ex->op_array == &closure->op_array) {
+                       zend_error(E_ERROR, "Cannot destroy active lambda 
function");
+               }
+               ex = ex->prev_execute_data;
+       }
+       destroy_op_array(&closure->op_array TSRMLS_CC);
+       if (closure->This) {
+               zval_ptr_dtor(&closure->This);
+       }
+       efree(closure);
+}
+
+
 int zend_init_rsrc_list_dtors(void)
 {
        int retval;
@@ -340,6 +361,8 @@ int zend_init_rsrc_list_dtors(void)
        retval = zend_hash_init(&list_destructors, 50, NULL, NULL, 1);
        list_destructors.nNextFreeElement=1;    /* we don't want resource type 
0 */
 
+       le_lambda_func = 
zend_register_list_destructors_ex(zend_lambda_func_resource_dtor, NULL, "lambda 
function", 0);
+
        return retval;
 }
 
@@ -364,7 +387,6 @@ char *zend_rsrc_list_get_rsrc_type(int r
                return NULL;
        }
 }
-
 /*
  * Local variables:
  * tab-width: 4
Index: Zend/zend_list.h
===================================================================
RCS file: /repository/ZendEngine2/zend_list.h,v
retrieving revision 1.48.2.1.2.1.2.1
diff -u -p -d -r1.48.2.1.2.1.2.1 zend_list.h
--- Zend/zend_list.h    31 Dec 2007 07:17:04 -0000      1.48.2.1.2.1.2.1
+++ Zend/zend_list.h    20 Jun 2008 10:57:09 -0000
@@ -86,6 +86,7 @@ ZEND_API char *zend_rsrc_list_get_rsrc_t
 ZEND_API int zend_fetch_list_dtor_id(char *type_name);
 
 extern ZEND_API int le_index_ptr;  /* list entry type for index pointers */
+extern ZEND_API int le_lambda_func;  /* list entry type for lambda functions */
 
 #define ZEND_VERIFY_RESOURCE(rsrc)             \
        if (!rsrc) {                                            \
Index: Zend/zend_vm_def.h
===================================================================
RCS file: /repository/ZendEngine2/zend_vm_def.h,v
retrieving revision 1.59.2.29.2.48.2.58
diff -u -p -d -r1.59.2.29.2.48.2.58 zend_vm_def.h
--- Zend/zend_vm_def.h  11 Jun 2008 13:18:39 -0000      1.59.2.29.2.48.2.58
+++ Zend/zend_vm_def.h  20 Jun 2008 10:57:09 -0000
@@ -2028,6 +2028,20 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_N
                }
        } else {
                function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
+               zend_closure *closure;
+
+               if (Z_TYPE_P(function_name) == IS_RESOURCE &&
+                   (closure = (zend_closure*) zend_fetch_resource 
(&function_name TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func)) != NULL) {
+                       EX(fbc) = (zend_function*)&closure->op_array;
+                       if ((EX(object) = closure->This) != NULL) {
+                               Z_ADDREF_P(EX(object));
+                               EX(called_scope) = Z_OBJCE_P(EX(object));
+                       } else {
+                               EX(called_scope) = closure->op_array.scope;
+                       }
+                       FREE_OP2();
+                       ZEND_VM_NEXT_OPCODE();
+               }
 
                if (Z_TYPE_P(function_name) != IS_STRING) {
                        zend_error_noreturn(E_ERROR, "Function name must be a 
string");
@@ -4327,4 +4341,42 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST,
        ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, CONST)
+{
+       zend_op *opline = EX(opline);
+       zend_op_array *op_array;
+       zend_closure  *closure;
+
+       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");
+       }
+       
+       closure = emalloc(sizeof(zend_closure));
+       closure->op_array = *op_array;
+
+       if (closure->op_array.static_variables) {
+               HashTable *static_variables = 
closure->op_array.static_variables;
+       
+               ALLOC_HASHTABLE(closure->op_array.static_variables);
+               zend_hash_init(closure->op_array.static_variables, 
zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
+               zend_hash_apply_with_arguments(static_variables, 
(apply_func_args_t)zval_copy_static_var, 1, closure->op_array.static_variables);
+       }
+
+       if ((closure->op_array.scope = EG(scope)) != NULL) {
+               closure->op_array.fn_flags |= ZEND_ACC_PUBLIC;
+               if ((closure->This = EG(This)) != NULL) {
+                       Z_ADDREF_P(closure->This);
+               } else {
+                       closure->op_array.fn_flags |= ZEND_ACC_STATIC;
+               }
+       } else {
+               closure->This = NULL;
+       }
+       (*closure->op_array.refcount)++;
+
+       ZEND_REGISTER_RESOURCE(&EX_T(opline->result.u.var).tmp_var, closure, 
le_lambda_func);
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper)
Index: Zend/zend_vm_execute.h
===================================================================
RCS file: /repository/ZendEngine2/zend_vm_execute.h,v
retrieving revision 1.62.2.30.2.49.2.58
diff -u -p -d -r1.62.2.30.2.49.2.58 zend_vm_execute.h
--- Zend/zend_vm_execute.h      11 Jun 2008 13:18:39 -0000      
1.62.2.30.2.49.2.58
+++ Zend/zend_vm_execute.h      20 Jun 2008 10:57:11 -0000
@@ -746,6 +746,20 @@ static int ZEND_FASTCALL  ZEND_INIT_FCAL
                }
        } else {
                function_name = &opline->op2.u.constant;
+               zend_closure *closure;
+
+               if (Z_TYPE_P(function_name) == IS_RESOURCE &&
+                   (closure = (zend_closure*) zend_fetch_resource 
(&function_name TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func)) != NULL) {
+                       EX(fbc) = (zend_function*)&closure->op_array;
+                       if ((EX(object) = closure->This) != NULL) {
+                               Z_ADDREF_P(EX(object));
+                               EX(called_scope) = Z_OBJCE_P(EX(object));
+                       } else {
+                               EX(called_scope) = closure->op_array.scope;
+                       }
+
+                       ZEND_VM_NEXT_OPCODE();
+               }
 
                if (Z_TYPE_P(function_name) != IS_STRING) {
                        zend_error_noreturn(E_ERROR, "Function name must be a 
string");
@@ -934,6 +948,20 @@ static int ZEND_FASTCALL  ZEND_INIT_FCAL
                }
        } else {
                function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), 
&free_op2 TSRMLS_CC);
+               zend_closure *closure;
+
+               if (Z_TYPE_P(function_name) == IS_RESOURCE &&
+                   (closure = (zend_closure*) zend_fetch_resource 
(&function_name TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func)) != NULL) {
+                       EX(fbc) = (zend_function*)&closure->op_array;
+                       if ((EX(object) = closure->This) != NULL) {
+                               Z_ADDREF_P(EX(object));
+                               EX(called_scope) = Z_OBJCE_P(EX(object));
+                       } else {
+                               EX(called_scope) = closure->op_array.scope;
+                       }
+                       zval_dtor(free_op2.var);
+                       ZEND_VM_NEXT_OPCODE();
+               }
 
                if (Z_TYPE_P(function_name) != IS_STRING) {
                        zend_error_noreturn(E_ERROR, "Function name must be a 
string");
@@ -1030,6 +1058,20 @@ static int ZEND_FASTCALL  ZEND_INIT_FCAL
                }
        } else {
                function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), 
&free_op2 TSRMLS_CC);
+               zend_closure *closure;
+
+               if (Z_TYPE_P(function_name) == IS_RESOURCE &&
+                   (closure = (zend_closure*) zend_fetch_resource 
(&function_name TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func)) != NULL) {
+                       EX(fbc) = (zend_function*)&closure->op_array;
+                       if ((EX(object) = closure->This) != NULL) {
+                               Z_ADDREF_P(EX(object));
+                               EX(called_scope) = Z_OBJCE_P(EX(object));
+                       } else {
+                               EX(called_scope) = closure->op_array.scope;
+                       }
+                       if (free_op2.var) {zval_ptr_dtor(&free_op2.var);};
+                       ZEND_VM_NEXT_OPCODE();
+               }
 
                if (Z_TYPE_P(function_name) != IS_STRING) {
                        zend_error_noreturn(E_ERROR, "Function name must be a 
string");
@@ -1154,6 +1196,20 @@ static int ZEND_FASTCALL  ZEND_INIT_FCAL
                }
        } else {
                function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R 
TSRMLS_CC);
+               zend_closure *closure;
+
+               if (Z_TYPE_P(function_name) == IS_RESOURCE &&
+                   (closure = (zend_closure*) zend_fetch_resource 
(&function_name TSRMLS_CC, -1, NULL, NULL, 1, le_lambda_func)) != NULL) {
+                       EX(fbc) = (zend_function*)&closure->op_array;
+                       if ((EX(object) = closure->This) != NULL) {
+                               Z_ADDREF_P(EX(object));
+                               EX(called_scope) = Z_OBJCE_P(EX(object));
+                       } else {
+                               EX(called_scope) = closure->op_array.scope;
+                       }
+
+                       ZEND_VM_NEXT_OPCODE();
+               }
 
                if (Z_TYPE_P(function_name) != IS_STRING) {
                        zend_error_noreturn(E_ERROR, "Function name must be a 
string");
@@ -2875,6 +2931,44 @@ static int ZEND_FASTCALL  ZEND_DECLARE_C
        ZEND_VM_NEXT_OPCODE();
 }
 
+static int ZEND_FASTCALL  
ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       zend_op *opline = EX(opline);
+       zend_op_array *op_array;
+       zend_closure  *closure;
+
+       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");
+       }
+
+       closure = emalloc(sizeof(zend_closure));
+       closure->op_array = *op_array;
+
+       if (closure->op_array.static_variables) {
+               HashTable *static_variables = 
closure->op_array.static_variables;
+
+               ALLOC_HASHTABLE(closure->op_array.static_variables);
+               zend_hash_init(closure->op_array.static_variables, 
zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
+               zend_hash_apply_with_arguments(static_variables, 
(apply_func_args_t)zval_copy_static_var, 1, closure->op_array.static_variables);
+       }
+
+       if ((closure->op_array.scope = EG(scope)) != NULL) {
+               closure->op_array.fn_flags |= ZEND_ACC_PUBLIC;
+               if ((closure->This = EG(This)) != NULL) {
+                       Z_ADDREF_P(closure->This);
+               } else {
+                       closure->op_array.fn_flags |= ZEND_ACC_STATIC;
+               }
+       } else {
+               closure->This = NULL;
+       }
+       (*closure->op_array.refcount)++;
+
+       ZEND_REGISTER_RESOURCE(&EX_T(opline->result.u.var).tmp_var, closure, 
le_lambda_func);
+       ZEND_VM_NEXT_OPCODE();
+}
+
 static int ZEND_FASTCALL  
ZEND_ADD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        zend_op *opline = EX(opline);
@@ -33508,6 +33602,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_JMP_SET_SPEC_CV_HANDLER,
        ZEND_JMP_SET_SPEC_CV_HANDLER,
        ZEND_JMP_SET_SPEC_CV_HANDLER,
+       ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_CONST_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER
   };
   zend_opcode_handlers = (opcode_handler_t*)labels;
Index: Zend/zend_vm_opcodes.h
===================================================================
RCS file: /repository/ZendEngine2/zend_vm_opcodes.h,v
retrieving revision 1.42.2.17.2.1.2.6
diff -u -p -d -r1.42.2.17.2.1.2.6 zend_vm_opcodes.h
--- Zend/zend_vm_opcodes.h      28 Mar 2008 14:35:01 -0000      
1.42.2.17.2.1.2.6
+++ Zend/zend_vm_opcodes.h      20 Jun 2008 10:57:11 -0000
@@ -152,3 +152,4 @@
 #define ZEND_HANDLE_EXCEPTION                149
 #define ZEND_USER_OPCODE                     150
 #define ZEND_JMP_SET                         152
+#define ZEND_DECLARE_LAMBDA_FUNCTION         153
Index: ext/pcre/php_pcre.c
===================================================================
RCS file: /repository/php-src/ext/pcre/php_pcre.c,v
retrieving revision 1.168.2.9.2.21.2.15
diff -u -p -d -r1.168.2.9.2.21.2.15 php_pcre.c
--- ext/pcre/php_pcre.c 1 Jun 2008 18:47:20 -0000       1.168.2.9.2.21.2.15
+++ ext/pcre/php_pcre.c 20 Jun 2008 10:57:11 -0000
@@ -1312,7 +1312,7 @@ static void preg_replace_impl(INTERNAL_F
        }
 
        SEPARATE_ZVAL(replace);
-       if (Z_TYPE_PP(replace) != IS_ARRAY)
+       if (Z_TYPE_PP(replace) != IS_ARRAY && Z_TYPE_PP(replace) != IS_RESOURCE)
                convert_to_string_ex(replace);
        if (is_callable_replace) {
                if (!zend_is_callable(*replace, 0, &callback_name)) {
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to