Hi Christian,
I took a look into your patch and found it too difficult.
So I implemented another patch (attached) which is based on your ideas.
>From user's level of view it does exactly the same except for "lexical"
variables definition. I don't use any new reserver word because every
new reserved word is going to break some user code. I use the special
syntax for lambda function definition instead, which looks much clear
for me. The following code creates a lambda function with arguments $x,
$y and lexical variables $a, $b, $c.
$a = function($x, $y | $a, $b $c) {};
The patch shouldn't affect opcode caches and other extensions as it
doesn't change any structures. It uses the op_array->static_variables
for lexical variables.
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. Now the following example works fine.
<?php
class X {
private function foo($x) {
echo $x;
}
function bar($s) {
return function ($x | $s) {
static $n = 0;
$n++;
$s = $n.':'.$s;
$this->foo($x[0].':'.$s);
};
}
}
$x = new X;
$x = $x->bar("bye\n");
$s = 'abc';
preg_replace_callback('/[abc]/', $x, $s);
?>
It prints:
a:1:bye
b:2:1:bye
c:3:2:1:bye
Of course the patch doesn't break any existent tests.
Please review.
Thanks. Dmitry.
Christian Seiler wrote:
> Hi,
>
> As a followup to the discussion in January, I'd like post a revised
> patch to
> this list that implements closures and anonymous functions in PHP.
>
> INTRODUCTION
> ------------
>
> Closures and lambda functions can make programming much easier in
> several ways:
>
> 1. Lambda functions allow the quick definition of throw-away functions
> that are not used elsewhere. Imaging for example a piece of code that
> needs to call preg_replace_callback(). Currently, there are three
> possibilities to acchieve this:
>
> a. Define the callback function elsewhere. This distributes code that
> belongs together throughout the file and decreases readability.
>
> b. Define the callback function in-place (but with a name). In that
> case
> one has to use function_exists() to make sure the function is only
> defined once. Example code:
>
> <?php
> function replace_spaces ($text) {
> if (!function_exists ('replace_spaces_helper')) {
> function replace_spaces_helper ($matches) {
> return str_replace ($matches[1], ' ', ' ').' ';
> }
> }
> return preg_replace_callback ('/( +) /',
> 'replace_spaces_helper',
> $text);
> }
> ?>
>
> Here, the additional if() around the function definition makes the
> source code difficult to read.
>
> c. Use the present create_function() in order to create a function at
> runtime. This approach has several disadvantages: First of all,
> syntax
> highlighting does not work because a string is passed to the
> function.
> It also compiles the function at run time and not at compile
> time so
> opcode caches can't cache the function.
>
> 2. Closures provide a very useful tool in order to make lambda
> functions even
> more useful. Just imagine you want to replace 'hello' through
> 'goodbye' in
> all elements of an array. PHP provides the array_map() function which
> accepts a callback. If you don't wan't to hard-code 'hello' and
> 'goodbye'
> into your sourcecode, you have only four choices:
>
> a. Use create_function(). But then you may only pass literal values
> (strings, integers, floats) into the function, objects at best as
> clones (if var_export() allows for it) and resources not at all.
> And
> you have to worry about escaping everything correctly.
> Especially when
> handling user input this can lead to all sorts of security issues.
>
> b. Write a function that uses global variables. This is ugly,
> non-reentrant and bad style.
>
> c. Create an entire class, instantiate it and pass the member function
> as a callback. This is perhaps the cleanest solution for this
> problem
> with current PHP but just think about it: Creating an entire
> class for
> this extremely simple purpose and nothing else seems overkill.
>
> d. Don't use array_map() but simply do it manually (foreach). In this
> simple case it may not be that much of an issue (because one simply
> wants to iterate over an array) but there are cases where doing
> something manually that a function with a callback as parameter
> does
> for you is quite tedious.
>
> [Yes, I know that str_replace also accepts arrays as a third
> parameter so
> this example may be a bit useless. But imagine you want to do a more
> complex operation than simple search and replace.]
>
> PROPOSED PATCH
> --------------
>
> I now propose a patch that implements compile-time lambda functions and
> closures for PHP while keeping the patch as simple as possible. The
> patch is
> based on a previous patch on mine which was based on ideas discussed here
> end of December / start of January.
>
> Userland perspective
> --------------------
>
> 1. The patch adds the following syntax as a valid expression:
>
> function & (parameters) { body }
>
> (The & is optional and indicates - just as with normal functions - that the
> anonymous function returns a reference instead of a value)
>
> Example usage:
>
> $lambda = function () { echo "Hello World!\n"; };
>
> The variable $lambda then contains a callable resource that may be called
> through different means:
>
> $lambda ();
> call_user_func ($lambda);
> call_user_func_array ($lambda, array ());
>
> This allows for simple lambda functions, for example:
>
> function replace_spaces ($text) {
> $replacement = function ($matches) {
> return str_replace ($matches[1], ' ', ' ').' ';
> };
> return preg_replace_callback ('/( +) /', $replacement, $text);
> }
>
> 2. The patch implements closures by defining an additional keyword
> 'lexical'
> that allows an lambda function (and *only* an lambda function) to import
> a variable from the "parent scope" to the lambda function scope. Example:
>
> function replace_in_array ($search, $replacement, $array) {
> $map = function ($text) {
> lexical $search, $replacement;
> if (strpos ($text, $search) > 50) {
> return str_replace ($search, $replacement, $text);
> } else {
> return $text;
> }
> };
> return array_map ($map, array);
> }
>
> The variables $search and $replacement are variables in the scope of the
> function replace_in_array() and the lexical keyword imports these variables
> into the scope of the closure. The variables are imported as a reference,
> so any change in the closure will result in a change in the variable of the
> function itself.
>
> 3. If a closure is defined inside an object, the closure has full access
> to the current object through $this (without the need to use 'lexical' to
> import it seperately) and all private and protected methods of that class.
> This also applies to nested closures. Essentially, closures inside
> methods are
> added as public methods to the class that contains the original method.
>
> 4. Closures may live longer as the methods that declared them. It is
> perfectly
> possible to have something like this:
>
> function getAdder($x) {
> return function ($y) {
> lexical $x;
> return $x + $y;
> };
> }
>
> Zend internal perspective
> -------------------------
>
> The patch basically changes the following in the Zend engine:
>
> When the compiler reaches a lambda function, it creates a unique name
> for that
> function ("\0__compiled_lambda_FILENAME_N" where FILENAME is the name of
> the
> file currently processed and N is a per-file counter). The use of the
> filename
> in the function name ensures compability with opcode caches. The lambda
> function is then immediately added to the function table (either the global
> function table or that of the current class if declared inside a class
> method).
> Instead of a normal ZEND_DECLARE_FUNCTION opcode the new
> ZEND_DECLARE_LAMBDA_FUNC is used as an opcode at this point. The op_array
> of the new function is initialized with is_lambda = 1 and is_closure = 0.
>
> When parsing a 'lexical' declaration inside an anonymous function the
> parser
> saves the name of the variable that is to be imported in an array stored
> as a member of the op_array structure (lexical_names).
>
> The opcode handler for ZEND_DECLARE_LAMBDA_FUNC does the following:
> First of
> all it creates a new op_array and copies the entire memory structure of the
> lambda function into it (the opcodes themselves are not copied since they
> are only referenced in the op_array structure). Then it sets is_closure = 1
> on the new op_array, and for each lexical variable name that the compiler
> added to the original op_array it creates a reference to that variable from
> the current scope into a HashTable member in the new op_array. It also
> saves
> the current object pointer ($this) as a member of the op_array in order to
> allow for the closure to access $this. Finally it registers the new
> op_array
> as a resource and returns that resource.
>
> The opcode handler of the 'lexical' construct simply fetches the variable
> from that HashTable and imports it into local scope of the inner function
> (just like with 'global' only with a different hash table).
>
> Some hooks were added that allow the 'lambda function' resource to be
> called.
> Also, there are several checks in place that make sure the lambda function
> is not called directly, i.e. if someone explicitely tries to use the
> internal
> function name instead of using the resource return value of the
> declaration.
>
> The patch
> ---------
>
> The patch is available here:
> <http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-16-1.diff>
>
> Please note that I did NOT include the contents of zend_language_scanner.c
> in the patch since that can easily be regenerated and just takes up
> enormous
> amounts of space.
>
> The patch itself applies against the 5.3 branch of PHP.
>
> If I understand the discussion regarding PHP6 on this list correctly, some
> people are currently undergoing the task of removing the unicode_semantics
> switch and if (UG(unicode)). As soon as this task is finished I will also
> provide a patch for CVS HEAD (it doesn't make much sense adopting the patch
> now and then having to change it again completely afterwards).
>
> BC BREAKS
> ---------
>
> * Introduction of a new keyword 'lexical'. Since it is very improbable
> that
> someone should use it as a function, method, class or property name, I
> think this is an acceptable break.
>
> Other that that, I can find no BC breaks of my patch.
>
> CAVEATS / POSSIBLE WTFS
> -----------------------
>
> * On writing $func = function () { }; there is a semicolon necessary.
> If left
> out it will produce a compile error. Since any attempt to remove that
> necessity would unecessarily bloat the grammar, I suggest we simply keep
> it the way it is. Also, Lukas Kahwe Smith pointed out that a single
> trailing semicolon after a closing brace already exists: do { } while
> ();
>
> * The fact that 'lexical' creates references may cause certain WTFs:
>
> for ($i = 0; $i < 10; $i++) {
> $arr[$i] = function () { lexical $i; return $i; };
> }
>
> This will not work as expected since $i is a reference and thus all
> created closures would reference the same variable. In order to get this
> right one has to do:
>
> for ($i = 0; $i < 10; $i++) {
> $loopIndex = $i;
> $arr[$i] = function () { lexical $loopIndex; return $loopIndex; };
> unset ($loopIndex);
> }
>
> This can be a WTF for people that don't expect lexical to create an
> actual reference, especially since other languages such as JavaScript
> don't do it. On the other hand, global and static both DO create
> references so that behaviour is consistent with current PHP.
>
> But complex constructions such as this will probably not be used by
> beginners so maintaining a good documentation should solve this.
>
> * The fact that 'lexical' is needed at all may cause WTFs. Other languages
> such as JavaScript implicitely have the entire scope visible to child
> functions. But since PHP does the same thing with global variables, I
> find a keyword like 'lexical' much more consistent than importing the
> entire scope (and always importing the entire scope costs unnecessary
> performance).
>
> FINAL THOUGHTS
> --------------
>
> My now proposed patch addresses the two main problems of my previous patch:
> Support for closures in objects (with access to $this) and opcode
> caches. My
> patch applies against PHP_5_3 and does not break any tests. It adds a
> valuable
> new language feature which I'd like to see in PHP.
>
> 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 19 Jun 2008 10:04:37 -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 19 Jun 2008 10:04:37 -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 19 Jun 2008 10:04:37 -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 19 Jun 2008 10:04:38 -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 19 Jun 2008 10:04:38 -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 19 Jun 2008 10:04:38 -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 */
+ | '|' 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 19 Jun 2008 10:04:38 -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,18 @@ 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;
+ destroy_zend_function(&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 +353,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 +379,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 19 Jun 2008 10:04:38 -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 19 Jun 2008 10:04:38 -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 19 Jun 2008 10:04:40 -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 19 Jun 2008 10:04:40 -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 19 Jun 2008 10:04:40 -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