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], ' ', '&nbsp;').' ';
>                 }
>               }
>               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], ' ', '&nbsp;').' ';
>   };
>   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 = &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 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

Reply via email to