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