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 = ¤t_op_array->opcodes[current_op_number]; + current_op->opcode = ZEND_DECLARE_LAMBDA_FUNCTION; + zval_dtor(¤t_op->op2.u.constant); + ZVAL_LONG(¤t_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