mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Added support for lambda functions and closures
This commit is contained in:
parent
d23342397c
commit
d5ef2f466c
35 changed files with 1126 additions and 12 deletions
1
NEWS
1
NEWS
|
@ -17,6 +17,7 @@ PHP NEWS
|
|||
- Changed mhash to be a wrapper layer around the hash extension. (Scott)
|
||||
|
||||
- Improved PHP syntax and semantics:
|
||||
. Added lambda functions and closures (Christian Seiler, Dmitry)
|
||||
. Added "jump label" operator (limited "goto"). (Dmitry, Sara)
|
||||
. Added NOWDOC syntax. (Gwynne Raskind, Stas, Dmitry)
|
||||
. Added HEREDOC syntax with double quotes. (Lars Strojny, Felipe)
|
||||
|
|
|
@ -17,7 +17,7 @@ libZend_la_SOURCES=\
|
|||
zend_objects_API.c zend_ts_hash.c zend_stream.c \
|
||||
zend_default_classes.c \
|
||||
zend_iterators.c zend_interfaces.c zend_exceptions.c \
|
||||
zend_strtod.c
|
||||
zend_strtod.c zend_closures.c
|
||||
|
||||
libZend_la_LDFLAGS =
|
||||
libZend_la_LIBADD = @ZEND_EXTRA_LIBS@
|
||||
|
|
|
@ -123,6 +123,10 @@ SOURCE=.\zend_builtin_functions.c
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\zend_closures.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\zend_compile.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
|
@ -148,6 +148,10 @@ SOURCE=.\zend_builtin_functions.c
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\zend_closures.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\zend_compile.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
30
Zend/tests/closure_001.phpt
Normal file
30
Zend/tests/closure_001.phpt
Normal file
|
@ -0,0 +1,30 @@
|
|||
--TEST--
|
||||
Closure 001: Lambda without lexical variables
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$lambda1 = function () {
|
||||
echo "Hello World!\n";
|
||||
};
|
||||
|
||||
$lambda2 = function ($x) {
|
||||
echo "Hello $x!\n";
|
||||
};
|
||||
|
||||
var_dump(is_callable($lambda1));
|
||||
var_dump(is_callable($lambda2));
|
||||
$lambda1();
|
||||
$lambda2("Universe");
|
||||
call_user_func($lambda1);
|
||||
call_user_func($lambda2, "Universe");
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
Hello World!
|
||||
Hello Universe!
|
||||
Hello World!
|
||||
Hello Universe!
|
||||
Done
|
29
Zend/tests/closure_002.phpt
Normal file
29
Zend/tests/closure_002.phpt
Normal file
|
@ -0,0 +1,29 @@
|
|||
--TEST--
|
||||
Closure 002: Lambda with lexical variables (global scope)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$x = 4;
|
||||
|
||||
$lambda1 = function () use ($x) {
|
||||
echo "$x\n";
|
||||
};
|
||||
|
||||
$lambda2 = function () use (&$x) {
|
||||
echo "$x\n";
|
||||
};
|
||||
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
$x++;
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
4
|
||||
4
|
||||
4
|
||||
5
|
||||
Done
|
33
Zend/tests/closure_003.phpt
Normal file
33
Zend/tests/closure_003.phpt
Normal file
|
@ -0,0 +1,33 @@
|
|||
--TEST--
|
||||
Closure 003: Lambda with lexical variables (local scope)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function run () {
|
||||
$x = 4;
|
||||
|
||||
$lambda1 = function () use ($x) {
|
||||
echo "$x\n";
|
||||
};
|
||||
|
||||
$lambda2 = function () use (&$x) {
|
||||
echo "$x\n";
|
||||
};
|
||||
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
$x++;
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
4
|
||||
4
|
||||
4
|
||||
5
|
||||
Done
|
35
Zend/tests/closure_004.phpt
Normal file
35
Zend/tests/closure_004.phpt
Normal file
|
@ -0,0 +1,35 @@
|
|||
--TEST--
|
||||
Closure 004: Lambda with lexical variables (scope lifetime)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function run () {
|
||||
$x = 4;
|
||||
|
||||
$lambda1 = function () use ($x) {
|
||||
echo "$x\n";
|
||||
};
|
||||
|
||||
$lambda2 = function () use (&$x) {
|
||||
echo "$x\n";
|
||||
$x++;
|
||||
};
|
||||
|
||||
return array($lambda1, $lambda2);
|
||||
}
|
||||
|
||||
list ($lambda1, $lambda2) = run();
|
||||
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
$lambda1();
|
||||
$lambda2();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
4
|
||||
4
|
||||
4
|
||||
5
|
||||
Done
|
74
Zend/tests/closure_005.phpt
Normal file
74
Zend/tests/closure_005.phpt
Normal file
|
@ -0,0 +1,74 @@
|
|||
--TEST--
|
||||
Closure 005: Lambda inside class, lifetime of $this
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
private $x;
|
||||
|
||||
function __construct($x) {
|
||||
$this->x = $x;
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
echo "Destroyed\n";
|
||||
}
|
||||
|
||||
function getIncer($val) {
|
||||
return function() use ($val) {
|
||||
$this->x += $val;
|
||||
};
|
||||
}
|
||||
|
||||
function getPrinter() {
|
||||
return function() {
|
||||
echo $this->x."\n";
|
||||
};
|
||||
}
|
||||
|
||||
function getError() {
|
||||
return static function() {
|
||||
echo $this->x."\n";
|
||||
};
|
||||
}
|
||||
|
||||
function printX() {
|
||||
echo $this->x."\n";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A(3);
|
||||
$incer = $a->getIncer(2);
|
||||
$printer = $a->getPrinter();
|
||||
$error = $a->getError();
|
||||
|
||||
$a->printX();
|
||||
$printer();
|
||||
$incer();
|
||||
$a->printX();
|
||||
$printer();
|
||||
|
||||
unset($a);
|
||||
|
||||
$incer();
|
||||
$printer();
|
||||
|
||||
unset($incer);
|
||||
$printer();
|
||||
|
||||
unset($printer);
|
||||
|
||||
$error();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECTF--
|
||||
3
|
||||
3
|
||||
5
|
||||
5
|
||||
7
|
||||
7
|
||||
Destroyed
|
||||
|
||||
Fatal error: Using $this when not in object context in %sclosure_005.php on line 28
|
19
Zend/tests/closure_006.phpt
Normal file
19
Zend/tests/closure_006.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
Closure 006: Nested lambdas
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$getClosure = function ($v) {
|
||||
return function () use ($v) {
|
||||
echo "Hello World: $v!\n";
|
||||
};
|
||||
};
|
||||
|
||||
$closure = $getClosure (2);
|
||||
$closure ();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Hello World: 2!
|
||||
Done
|
38
Zend/tests/closure_007.phpt
Normal file
38
Zend/tests/closure_007.phpt
Normal file
|
@ -0,0 +1,38 @@
|
|||
--TEST--
|
||||
Closure 007: Nested lambdas in classes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
private $x = 0;
|
||||
|
||||
function getClosureGetter () {
|
||||
return function () {
|
||||
return function () {
|
||||
$this->x++;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function printX () {
|
||||
echo $this->x."\n";
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A;
|
||||
$a->printX();
|
||||
$getClosure = $a->getClosureGetter();
|
||||
$a->printX();
|
||||
$closure = $getClosure();
|
||||
$a->printX();
|
||||
$closure();
|
||||
$a->printX();
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
0
|
||||
0
|
||||
0
|
||||
1
|
||||
Done
|
22
Zend/tests/closure_008.phpt
Normal file
22
Zend/tests/closure_008.phpt
Normal file
|
@ -0,0 +1,22 @@
|
|||
--TEST--
|
||||
Closure 008: Use in preg_replace()
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function replace_spaces($text) {
|
||||
$lambda = function ($matches) {
|
||||
return str_replace(' ', ' ', $matches[1]).' ';
|
||||
};
|
||||
return preg_replace_callback('/( +) /', $lambda, $text);
|
||||
}
|
||||
|
||||
echo replace_spaces("1 2 3\n");
|
||||
echo replace_spaces("1 2 3\n");
|
||||
echo replace_spaces("1 2 3\n");
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
1 2 3
|
||||
1 2 3
|
||||
1 2 3
|
||||
Done
|
31
Zend/tests/closure_009.phpt
Normal file
31
Zend/tests/closure_009.phpt
Normal file
|
@ -0,0 +1,31 @@
|
|||
--TEST--
|
||||
Closure 009: Use in preg_replace()
|
||||
--FILE--
|
||||
<?php
|
||||
$a = 1;
|
||||
$x = function ($x) use ($a) {
|
||||
static $n = 0;
|
||||
$n++;
|
||||
$a = $n.':'.$a;
|
||||
echo $x.':'.$a."\n";
|
||||
};
|
||||
$y = function ($x) use (&$a) {
|
||||
static $n = 0;
|
||||
$n++;
|
||||
$a = $n.':'.$a;
|
||||
echo $x.':'.$a."\n";
|
||||
};
|
||||
$x(1);
|
||||
$x(2);
|
||||
$x(3);
|
||||
$y(4);
|
||||
$y(5);
|
||||
$y(6);
|
||||
?>
|
||||
--EXPECT--
|
||||
1:1:1
|
||||
2:2:1
|
||||
3:3:1
|
||||
4:1:1
|
||||
5:2:1:1
|
||||
6:3:2:1:1
|
18
Zend/tests/closure_010.phpt
Normal file
18
Zend/tests/closure_010.phpt
Normal file
|
@ -0,0 +1,18 @@
|
|||
--TEST--
|
||||
Closure 010: Closure calls itself
|
||||
--FILE--
|
||||
<?php
|
||||
$i = 3;
|
||||
$lambda = function ($lambda) use (&$i) {
|
||||
if ($i==0) return;
|
||||
echo $i--."\n";
|
||||
$lambda($lambda);
|
||||
};
|
||||
$lambda($lambda);
|
||||
echo "$i\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
3
|
||||
2
|
||||
1
|
||||
0
|
14
Zend/tests/closure_011.phpt
Normal file
14
Zend/tests/closure_011.phpt
Normal file
|
@ -0,0 +1,14 @@
|
|||
--TEST--
|
||||
Closure 011: Lexical copies not static in closure
|
||||
--FILE--
|
||||
<?php
|
||||
$i = 1;
|
||||
$lambda = function () use ($i) {
|
||||
return ++$i;
|
||||
};
|
||||
$lambda();
|
||||
echo $lambda()."\n";
|
||||
//early prototypes gave 3 here because $i was static in $lambda
|
||||
?>
|
||||
--EXPECT--
|
||||
2
|
24
Zend/tests/closure_012.phpt
Normal file
24
Zend/tests/closure_012.phpt
Normal file
|
@ -0,0 +1,24 @@
|
|||
--TEST--
|
||||
Closure 012: Undefined lexical variables
|
||||
--FILE--
|
||||
<?php
|
||||
$lambda = function () use ($i) {
|
||||
return ++$i;
|
||||
};
|
||||
$lambda();
|
||||
$lambda();
|
||||
var_dump($i);
|
||||
$lambda = function () use (&$i) {
|
||||
return ++$i;
|
||||
};
|
||||
$lambda();
|
||||
$lambda();
|
||||
var_dump($i);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Notice: Undefined variable: i in %sclosure_012.php on line 2
|
||||
|
||||
Notice: Undefined variable: i in %sclosure_012.php on line 7
|
||||
NULL
|
||||
int(2)
|
||||
|
25
Zend/tests/closure_013.phpt
Normal file
25
Zend/tests/closure_013.phpt
Normal file
|
@ -0,0 +1,25 @@
|
|||
--TEST--
|
||||
Closure 013: __invoke() on temporary result
|
||||
--FILE--
|
||||
<?php
|
||||
class Foo {
|
||||
function __invoke() {
|
||||
echo "Hello World!\n";
|
||||
}
|
||||
}
|
||||
|
||||
function foo() {
|
||||
return function() {
|
||||
echo "Hello World!\n";
|
||||
};
|
||||
}
|
||||
$test = new Foo;
|
||||
$test->__invoke();
|
||||
$test = foo();
|
||||
$test->__invoke();
|
||||
$test = foo()->__invoke();
|
||||
?>
|
||||
--EXPECT--
|
||||
Hello World!
|
||||
Hello World!
|
||||
Hello World!
|
79
Zend/tests/closure_014.phpt
Normal file
79
Zend/tests/closure_014.phpt
Normal file
|
@ -0,0 +1,79 @@
|
|||
--TEST--
|
||||
Closure 014: return by value/reference
|
||||
--FILE--
|
||||
<?php
|
||||
class C1 {
|
||||
function __invoke() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
class C2 {
|
||||
function &__invoke(&$a) {
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
class C3 {
|
||||
function __invoke() {
|
||||
}
|
||||
}
|
||||
|
||||
$x = new C1();
|
||||
var_dump($x());
|
||||
var_dump($x->__invoke());
|
||||
$x();
|
||||
$x->__invoke();
|
||||
$x = function() {
|
||||
return 0;
|
||||
};
|
||||
var_dump($x());
|
||||
var_dump($x->__invoke());
|
||||
$x();
|
||||
$x->__invoke();
|
||||
|
||||
$x = new C2();
|
||||
$a = $b = $c = $d = 1;
|
||||
$e =& $x($a);
|
||||
$e = 2;
|
||||
var_dump($a);
|
||||
$e =& $x->__invoke($b);
|
||||
$e = 3;
|
||||
var_dump($b);
|
||||
$x($b);
|
||||
$x->__invoke($b);
|
||||
$x = function & (&$a) {
|
||||
return $a;
|
||||
};
|
||||
$e =& $x($c);
|
||||
$e = 4;
|
||||
var_dump($c);
|
||||
$e =& $x->__invoke($d);
|
||||
$e = 5;
|
||||
var_dump($d);
|
||||
$x($d);
|
||||
$x->__invoke($d);
|
||||
|
||||
$x = new C3();
|
||||
var_dump($x());
|
||||
var_dump($x->__invoke());
|
||||
$x();
|
||||
$x->__invoke();
|
||||
$x = function() {
|
||||
};
|
||||
var_dump($x());
|
||||
var_dump($x->__invoke());
|
||||
$x();
|
||||
$x->__invoke();
|
||||
?>
|
||||
--EXPECT--
|
||||
int(0)
|
||||
int(0)
|
||||
int(0)
|
||||
int(0)
|
||||
int(2)
|
||||
int(3)
|
||||
int(4)
|
||||
int(5)
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
||||
NULL
|
13
Zend/tests/closure_015.phpt
Normal file
13
Zend/tests/closure_015.phpt
Normal file
|
@ -0,0 +1,13 @@
|
|||
--TEST--
|
||||
Closure 015: converting to string/unicode
|
||||
--FILE--
|
||||
<?php
|
||||
$x = function() { return 1; };
|
||||
print (string) $x;
|
||||
print "\n";
|
||||
print $x;
|
||||
print "\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Closure object
|
||||
Closure object
|
|
@ -518,6 +518,8 @@ typedef int (*zend_write_func_t)(const char *str, uint str_length);
|
|||
#define IS_CONSTANT_TYPE_MASK 0x0f
|
||||
#define IS_CONSTANT_RT_NS_CHECK 0x10
|
||||
#define IS_CONSTANT_INDEX 0x80
|
||||
#define IS_LEXICAL_VAR 0x20
|
||||
#define IS_LEXICAL_REF 0x40
|
||||
|
||||
/* overloaded elements data types */
|
||||
#define OE_IS_ARRAY (1<<0)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "zend_modules.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_closures.h"
|
||||
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
|
@ -2615,6 +2616,16 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char **
|
|||
}
|
||||
return 0;
|
||||
|
||||
case IS_OBJECT:
|
||||
if (zend_get_closure(callable, ce_ptr, fptr_ptr, NULL, zobj_ptr_ptr TSRMLS_CC) == SUCCESS) {
|
||||
if (callable_name) {
|
||||
*callable_name_len = strlen((*fptr_ptr)->common.function_name);
|
||||
*callable_name = estrndup((*fptr_ptr)->common.function_name, *callable_name_len);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
|
||||
default:
|
||||
if (callable_name) {
|
||||
zval expr_copy;
|
||||
|
|
329
Zend/zend_closures.c
Normal file
329
Zend/zend_closures.c
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1998-2008 Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Christian Seiler <chris_se@gmx.net> |
|
||||
| Dmitry Stogov <dmitry@zend.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "zend.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_closures.h"
|
||||
#include "zend_objects.h"
|
||||
#include "zend_objects_API.h"
|
||||
#include "zend_globals.h"
|
||||
|
||||
#define ZEND_INVOKE_FUNC_NAME "__invoke"
|
||||
#define ZEND_CLOSURE_PRINT_NAME "Closure object"
|
||||
|
||||
typedef struct _zend_closure {
|
||||
zend_object std;
|
||||
zend_function func;
|
||||
zval *this_ptr;
|
||||
zend_function *invoke;
|
||||
} zend_closure;
|
||||
|
||||
static zend_class_entry *zend_ce_closure;
|
||||
static zend_object_handlers closure_handlers;
|
||||
|
||||
ZEND_METHOD(Closure, __invoke) /* {{{ */
|
||||
{
|
||||
zval ***arguments;
|
||||
zval *closure_result_ptr = NULL;
|
||||
|
||||
arguments = emalloc(sizeof(zval**) * ZEND_NUM_ARGS());
|
||||
if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
|
||||
efree(arguments);
|
||||
zend_error(E_ERROR, "Cannot get arguments for calling closure");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (call_user_function_ex(CG(function_table), NULL, this_ptr, &closure_result_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) {
|
||||
efree(arguments);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
efree(arguments);
|
||||
if (closure_result_ptr) {
|
||||
if (Z_ISREF_P(closure_result_ptr) && return_value_ptr) {
|
||||
if (return_value) {
|
||||
zval_ptr_dtor(&return_value);
|
||||
}
|
||||
*return_value_ptr = closure_result_ptr;
|
||||
} else {
|
||||
RETVAL_ZVAL(closure_result_ptr, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_error(E_ERROR, "Instantiation of 'Closure' is not allowed");
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_closure_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
return (Z_OBJ_HANDLE_P(o1) != Z_OBJ_HANDLE_P(o2));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_closure_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_class_entry *ce;
|
||||
|
||||
switch (type) {
|
||||
case IS_STRING:
|
||||
INIT_PZVAL(writeobj);
|
||||
ZVAL_STRINGL(writeobj, ZEND_CLOSURE_PRINT_NAME, sizeof(ZEND_CLOSURE_PRINT_NAME)-1, 1);
|
||||
return SUCCESS;
|
||||
default:
|
||||
ce = Z_OBJCE_P(readobj);
|
||||
zend_error(E_NOTICE, "Object of class %s could not be converted to %s", ce->name, zend_get_type_by_const(type));
|
||||
INIT_PZVAL(writeobj);
|
||||
Z_TYPE_P(writeobj) = IS_NULL;
|
||||
break;
|
||||
}
|
||||
return FAILURE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_function *zend_closure_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
char *lc_name;
|
||||
ALLOCA_FLAG(use_heap)
|
||||
|
||||
lc_name = do_alloca(method_len + 1, use_heap);
|
||||
zend_str_tolower_copy(lc_name, method_name, method_len);
|
||||
if ((method_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) &&
|
||||
memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0) {
|
||||
zend_closure *closure = (zend_closure *)zend_object_store_get_object(*object_ptr TSRMLS_CC);
|
||||
|
||||
if (!closure->invoke) {
|
||||
closure->invoke = (zend_function*)emalloc(sizeof(zend_function));
|
||||
closure->invoke->common = closure->func.common;
|
||||
closure->invoke->type = ZEND_INTERNAL_FUNCTION;
|
||||
closure->invoke->internal_function.handler = ZEND_MN(Closure___invoke);
|
||||
closure->invoke->internal_function.module = 0;
|
||||
closure->invoke->internal_function.scope = zend_ce_closure;
|
||||
closure->invoke->internal_function.function_name = ZEND_INVOKE_FUNC_NAME;
|
||||
}
|
||||
free_alloca(lc_name, use_heap);
|
||||
return (zend_function *)closure->invoke;
|
||||
}
|
||||
free_alloca(lc_name, use_heap);
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_closure *closure = (zend_closure *)object;
|
||||
|
||||
zend_object_std_dtor(&closure->std TSRMLS_CC);
|
||||
|
||||
if (closure->func.type == ZEND_USER_FUNCTION) {
|
||||
zend_execute_data *ex = EG(current_execute_data);
|
||||
while (ex) {
|
||||
if (ex->op_array == &closure->func.op_array) {
|
||||
zend_error(E_ERROR, "Cannot destroy active lambda function");
|
||||
}
|
||||
ex = ex->prev_execute_data;
|
||||
}
|
||||
destroy_op_array(&closure->func.op_array TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (closure->this_ptr) {
|
||||
zval_ptr_dtor(&closure->this_ptr);
|
||||
}
|
||||
|
||||
if (closure->invoke) {
|
||||
efree(closure->invoke);
|
||||
}
|
||||
|
||||
efree(closure);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_object_value zend_closure_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_closure *closure;
|
||||
zend_object_value object;
|
||||
|
||||
closure = emalloc(sizeof(zend_closure));
|
||||
memset(closure, 0, sizeof(zend_closure));
|
||||
|
||||
zend_object_std_init(&closure->std, class_type TSRMLS_CC);
|
||||
|
||||
object.handle = zend_objects_store_put(closure, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_closure_free_storage, NULL TSRMLS_CC);
|
||||
object.handlers = &closure_handlers;
|
||||
|
||||
return object;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_register_closure_ce(TSRMLS_D) /* {{{ */
|
||||
{
|
||||
zend_class_entry ce;
|
||||
|
||||
INIT_CLASS_ENTRY(ce, "Closure", NULL);
|
||||
zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC);
|
||||
zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS;
|
||||
zend_ce_closure->create_object = zend_closure_new;
|
||||
|
||||
memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
|
||||
closure_handlers.get_constructor = zend_closure_get_constructor;
|
||||
closure_handlers.get_method = zend_closure_get_method;
|
||||
closure_handlers.compare_objects = zend_closure_compare_objects;
|
||||
closure_handlers.cast_object = zend_closure_cast_object_tostring;
|
||||
closure_handlers.clone_obj = NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zval_copy_static_var(zval **p, int num_args, va_list args, zend_hash_key *key) /* {{{ */
|
||||
{
|
||||
HashTable *target = va_arg(args, HashTable*);
|
||||
zend_bool is_ref;
|
||||
|
||||
if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
|
||||
TSRMLS_FETCH();
|
||||
is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF;
|
||||
|
||||
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) {
|
||||
if (is_ref) {
|
||||
zval *tmp;
|
||||
|
||||
ALLOC_INIT_ZVAL(tmp);
|
||||
Z_SET_ISREF_P(tmp);
|
||||
zend_hash_quick_add(EG(active_symbol_table), key->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), (void**)&p);
|
||||
} else {
|
||||
p = &EG(uninitialized_zval_ptr);
|
||||
zend_error(E_NOTICE,"Undefined variable: %s", key->arKey);
|
||||
}
|
||||
} else {
|
||||
if (is_ref) {
|
||||
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
|
||||
} else if (Z_ISREF_PP(p)) {
|
||||
SEPARATE_ZVAL(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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zval *this_ptr TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_closure *closure;
|
||||
|
||||
object_init_ex(res, zend_ce_closure);
|
||||
|
||||
closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC);
|
||||
|
||||
closure->func = *func;
|
||||
|
||||
if (closure->func.type == ZEND_USER_FUNCTION) {
|
||||
if (closure->func.op_array.static_variables) {
|
||||
HashTable *static_variables = closure->func.op_array.static_variables;
|
||||
|
||||
ALLOC_HASHTABLE(closure->func.op_array.static_variables);
|
||||
zend_hash_init(closure->func.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->func.op_array.static_variables);
|
||||
}
|
||||
(*closure->func.op_array.refcount)++;
|
||||
}
|
||||
|
||||
closure->func.common.scope = scope;
|
||||
if (scope) {
|
||||
closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
|
||||
if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
|
||||
closure->this_ptr = this_ptr;
|
||||
Z_ADDREF_P(this_ptr);
|
||||
} else {
|
||||
closure->this_ptr = NULL;
|
||||
}
|
||||
} else {
|
||||
closure->this_ptr = NULL;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API int zend_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr, zval ***zobj_ptr_ptr TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
if (Z_TYPE_P(obj) == IS_OBJECT) {
|
||||
zend_class_entry *ce = Z_OBJCE_P(obj);
|
||||
|
||||
if (ce == zend_ce_closure) {
|
||||
zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
|
||||
|
||||
*fptr_ptr = &closure->func;
|
||||
if (closure->this_ptr) {
|
||||
if (zobj_ptr) {
|
||||
*zobj_ptr = closure->this_ptr;
|
||||
}
|
||||
if (zobj_ptr_ptr) {
|
||||
*zobj_ptr_ptr = &closure->this_ptr;
|
||||
}
|
||||
*ce_ptr = Z_OBJCE_P(closure->this_ptr);
|
||||
} else {
|
||||
if (zobj_ptr) {
|
||||
*zobj_ptr = NULL;
|
||||
}
|
||||
if (zobj_ptr_ptr) {
|
||||
*zobj_ptr_ptr = NULL;
|
||||
}
|
||||
*ce_ptr = closure->func.common.scope;
|
||||
}
|
||||
return SUCCESS;
|
||||
} else if (zend_hash_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME), (void**)fptr_ptr) == SUCCESS) {
|
||||
*ce_ptr = ce;
|
||||
if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
|
||||
if (zobj_ptr) {
|
||||
*zobj_ptr = NULL;
|
||||
}
|
||||
if (zobj_ptr_ptr) {
|
||||
*zobj_ptr_ptr = NULL;
|
||||
}
|
||||
} else {
|
||||
if (zobj_ptr) {
|
||||
*zobj_ptr = obj;
|
||||
}
|
||||
if (zobj_ptr_ptr) {
|
||||
*zobj_ptr_ptr = NULL;
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
return FAILURE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
42
Zend/zend_closures.h
Normal file
42
Zend/zend_closures.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1998-2008 Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Christian Seiler <chris_se@gmx.net> |
|
||||
| Dmitry Stogov <dmitry@zend.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef ZEND_CLOSURES_H
|
||||
#define ZEND_CLOSURES_H
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
void zend_register_closure_ce(TSRMLS_D);
|
||||
|
||||
ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zval *this_ptr TSRMLS_DC);
|
||||
ZEND_API int zend_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr, zval ***zobj_ptr_ptr TSRMLS_DC);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
|
@ -1401,6 +1401,33 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
|
|||
CG(labels) = NULL;
|
||||
}
|
||||
|
||||
void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static 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;
|
||||
if (is_static) {
|
||||
CG(active_op_array)->fn_flags |= ZEND_ACC_STATIC;
|
||||
}
|
||||
CG(active_op_array)->fn_flags |= ZEND_ACC_CLOSURE;
|
||||
}
|
||||
|
||||
|
||||
void zend_do_handle_exception(TSRMLS_D)
|
||||
{
|
||||
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
|
||||
|
@ -4155,13 +4182,13 @@ void zend_do_fetch_static_variable(znode *varname, znode *static_assignment, int
|
|||
}
|
||||
|
||||
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
|
||||
opline->opcode = ZEND_FETCH_W; /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */
|
||||
opline->opcode = (fetch_type == ZEND_FETCH_LEXICAL) ? ZEND_FETCH_R : ZEND_FETCH_W; /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */
|
||||
opline->result.op_type = IS_VAR;
|
||||
opline->result.u.EA.type = 0;
|
||||
opline->result.u.var = get_temporary_variable(CG(active_op_array));
|
||||
opline->op1 = *varname;
|
||||
SET_UNUSED(opline->op2);
|
||||
opline->op2.u.EA.type = fetch_type;
|
||||
opline->op2.u.EA.type = ZEND_FETCH_STATIC;
|
||||
result = opline->result;
|
||||
|
||||
if (varname->op_type == IS_CONST) {
|
||||
|
@ -4169,12 +4196,39 @@ void zend_do_fetch_static_variable(znode *varname, znode *static_assignment, int
|
|||
}
|
||||
fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */
|
||||
|
||||
zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC);
|
||||
if (fetch_type == ZEND_FETCH_LEXICAL) {
|
||||
znode dummy;
|
||||
|
||||
zend_do_begin_variable_parse(TSRMLS_C);
|
||||
zend_do_assign(&dummy, &lval, &result TSRMLS_CC);
|
||||
zend_do_free(&dummy TSRMLS_CC);
|
||||
} else {
|
||||
zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC);
|
||||
}
|
||||
CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED;
|
||||
|
||||
/* zval_dtor(&varname->u.constant); */
|
||||
}
|
||||
|
||||
void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC)
|
||||
{
|
||||
znode value;
|
||||
|
||||
if (Z_STRLEN(varname->u.constant) == sizeof("this") - 1 &&
|
||||
memcmp(Z_STRVAL(varname->u.constant), "this", sizeof("this") - 1) == 0) {
|
||||
zend_error(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
|
||||
return;
|
||||
}
|
||||
|
||||
value.op_type = IS_CONST;
|
||||
ZVAL_NULL(&value.u.constant);
|
||||
Z_TYPE(value.u.constant) |= is_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR;
|
||||
Z_SET_REFCOUNT_P(&value.u.constant, 1);
|
||||
Z_UNSET_ISREF_P(&value.u.constant);
|
||||
|
||||
zend_do_fetch_static_variable(varname, &value, is_ref ? ZEND_FETCH_STATIC : ZEND_FETCH_LEXICAL TSRMLS_CC);
|
||||
}
|
||||
|
||||
void zend_do_fetch_global_variable(znode *varname, znode *static_assignment, int fetch_type TSRMLS_DC)
|
||||
{
|
||||
zend_op *opline;
|
||||
|
|
|
@ -151,6 +151,8 @@ typedef struct _zend_try_catch_element {
|
|||
/* class implement interface(s) flag */
|
||||
#define ZEND_ACC_IMPLEMENT_INTERFACES 0x80000
|
||||
|
||||
#define ZEND_ACC_CLOSURE 0x100000
|
||||
|
||||
|
||||
char *zend_visibility_string(zend_uint fn_flags);
|
||||
|
||||
|
@ -429,6 +431,9 @@ void zend_do_end_function_call(znode *function_name, znode *result, znode *argum
|
|||
void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
|
||||
void zend_do_handle_exception(TSRMLS_D);
|
||||
|
||||
void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC);
|
||||
void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref 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);
|
||||
|
@ -614,7 +619,8 @@ int zendlex(znode *zendlval TSRMLS_DC);
|
|||
#define ZEND_FETCH_LOCAL 1
|
||||
#define ZEND_FETCH_STATIC 2
|
||||
#define ZEND_FETCH_STATIC_MEMBER 3
|
||||
#define ZEND_FETCH_GLOBAL_LOCK 4
|
||||
#define ZEND_FETCH_GLOBAL_LOCK 4
|
||||
#define ZEND_FETCH_LEXICAL 5
|
||||
|
||||
|
||||
/* class fetches */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "zend_builtin_functions.h"
|
||||
#include "zend_interfaces.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_closures.h"
|
||||
|
||||
|
||||
ZEND_API void zend_register_default_classes(TSRMLS_D)
|
||||
|
@ -31,6 +32,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D)
|
|||
zend_register_interfaces(TSRMLS_C);
|
||||
zend_register_default_exception(TSRMLS_C);
|
||||
zend_register_iterator_wrapper(TSRMLS_C);
|
||||
zend_register_closure_ce(TSRMLS_C);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "zend_ini.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_interfaces.h"
|
||||
#include "zend_closures.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
/* Virtual current working directory support */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "zend_constants.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_closures.h"
|
||||
#include "zend_vm.h"
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
|
@ -826,7 +827,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
|
|||
}
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(fci->function_name) != IS_STRING) {
|
||||
if (Z_TYPE_P(fci->function_name) == IS_OBJECT) {
|
||||
if (zend_get_closure(fci->function_name, &calling_scope, &EX(function_state).function, NULL, &fci->object_pp TSRMLS_CC) == SUCCESS) {
|
||||
goto init_fci_cache;
|
||||
}
|
||||
} else if (Z_TYPE_P(fci->function_name) != IS_STRING) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
|
@ -935,6 +940,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
|
|||
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)
|
||||
|
|
|
@ -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,10 +643,30 @@ 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, 0 TSRMLS_CC); }
|
||||
parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; }
|
||||
| T_STATIC function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); }
|
||||
parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; }
|
||||
;
|
||||
|
||||
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, 0 TSRMLS_CC); }
|
||||
| lexical_var_list ',' '&' T_VARIABLE { zend_do_fetch_lexical_variable(&$4, 1 TSRMLS_CC); }
|
||||
| T_VARIABLE { zend_do_fetch_lexical_variable(&$1, 0 TSRMLS_CC); }
|
||||
| '&' T_VARIABLE { zend_do_fetch_lexical_variable(&$2, 1 TSRMLS_CC); }
|
||||
;
|
||||
|
||||
function_call:
|
||||
T_STRING '(' { $2.u.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
|
||||
T_STRING '(' { $2.u.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
|
||||
function_call_parameter_list
|
||||
')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
|
||||
| T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' { $3.u.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); }
|
||||
|
|
|
@ -2029,6 +2029,15 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
|
|||
} else {
|
||||
function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
if (Z_TYPE_P(function_name) == IS_OBJECT &&
|
||||
zend_get_closure(function_name, &EX(called_scope), &EX(fbc), &EX(object), NULL TSRMLS_CC) == SUCCESS) {
|
||||
if (EX(object)) {
|
||||
Z_ADDREF_P(EX(object));
|
||||
}
|
||||
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 +4336,19 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, 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;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array, EG(scope), EG(This) TSRMLS_CC);
|
||||
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper)
|
||||
|
|
|
@ -747,6 +747,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
|
|||
} else {
|
||||
function_name = &opline->op2.u.constant;
|
||||
|
||||
if (Z_TYPE_P(function_name) == IS_OBJECT &&
|
||||
zend_get_closure(function_name, &EX(called_scope), &EX(fbc), &EX(object), NULL TSRMLS_CC) == SUCCESS) {
|
||||
if (EX(object)) {
|
||||
Z_ADDREF_P(EX(object));
|
||||
}
|
||||
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(function_name) != IS_STRING) {
|
||||
zend_error_noreturn(E_ERROR, "Function name must be a string");
|
||||
}
|
||||
|
@ -935,6 +944,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
|
|||
} else {
|
||||
function_name = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC);
|
||||
|
||||
if (Z_TYPE_P(function_name) == IS_OBJECT &&
|
||||
zend_get_closure(function_name, &EX(called_scope), &EX(fbc), &EX(object), NULL TSRMLS_CC) == SUCCESS) {
|
||||
if (EX(object)) {
|
||||
Z_ADDREF_P(EX(object));
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
@ -1031,6 +1049,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
|
|||
} else {
|
||||
function_name = _get_zval_ptr_var(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC);
|
||||
|
||||
if (Z_TYPE_P(function_name) == IS_OBJECT &&
|
||||
zend_get_closure(function_name, &EX(called_scope), &EX(fbc), &EX(object), NULL TSRMLS_CC) == SUCCESS) {
|
||||
if (EX(object)) {
|
||||
Z_ADDREF_P(EX(object));
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
@ -1155,6 +1182,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
|
|||
} else {
|
||||
function_name = _get_zval_ptr_cv(&opline->op2, EX(Ts), BP_VAR_R TSRMLS_CC);
|
||||
|
||||
if (Z_TYPE_P(function_name) == IS_OBJECT &&
|
||||
zend_get_closure(function_name, &EX(called_scope), &EX(fbc), &EX(object), NULL TSRMLS_CC) == SUCCESS) {
|
||||
if (EX(object)) {
|
||||
Z_ADDREF_P(EX(object));
|
||||
}
|
||||
|
||||
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 +2911,21 @@ static int ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCOD
|
|||
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;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array, EG(scope), EG(This) TSRMLS_CC);
|
||||
|
||||
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 +33559,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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1403,7 +1403,8 @@ PHP_ADD_SOURCES(Zend, \
|
|||
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
|
||||
zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \
|
||||
zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
|
||||
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c)
|
||||
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \
|
||||
zend_closures.c)
|
||||
|
||||
if test -r "$abs_srcdir/Zend/zend_objects.c"; then
|
||||
PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c \
|
||||
|
|
|
@ -1312,7 +1312,7 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_callabl
|
|||
}
|
||||
|
||||
SEPARATE_ZVAL(replace);
|
||||
if (Z_TYPE_PP(replace) != IS_ARRAY)
|
||||
if (Z_TYPE_PP(replace) != IS_ARRAY && (Z_TYPE_PP(replace) != IS_OBJECT || !is_callable_replace))
|
||||
convert_to_string_ex(replace);
|
||||
if (is_callable_replace) {
|
||||
if (!zend_is_callable(*replace, 0, &callback_name)) {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "zend_constants.h"
|
||||
#include "zend_ini.h"
|
||||
#include "zend_interfaces.h"
|
||||
#include "zend_closures.h"
|
||||
|
||||
/* Undefine "getParameters" macro defined in "main/php3_compat.h" */
|
||||
#ifdef getParameters
|
||||
|
@ -1562,6 +1563,20 @@ ZEND_METHOD(reflection_function, getStaticVariables)
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public mixed ReflectionFunction::getClosure()
|
||||
Invokes the function */
|
||||
ZEND_METHOD(reflection_function, getClosure)
|
||||
{
|
||||
reflection_object *intern;
|
||||
zend_function *fptr;
|
||||
|
||||
METHOD_NOTSTATIC_NUMPARAMS(reflection_function_ptr, 0);
|
||||
GET_REFLECTION_OBJECT_PTR(fptr);
|
||||
|
||||
zend_create_closure(return_value, fptr, NULL, NULL TSRMLS_CC);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public mixed ReflectionFunction::invoke(mixed* args)
|
||||
Invokes the function */
|
||||
ZEND_METHOD(reflection_function, invoke)
|
||||
|
@ -2290,6 +2305,34 @@ ZEND_METHOD(reflection_method, __toString)
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public mixed ReflectionMethod::getClosure([mixed object])
|
||||
Invokes the function */
|
||||
ZEND_METHOD(reflection_method, getClosure)
|
||||
{
|
||||
reflection_object *intern;
|
||||
zval *obj;
|
||||
zend_function *mptr;
|
||||
|
||||
METHOD_NOTSTATIC(reflection_method_ptr);
|
||||
GET_REFLECTION_OBJECT_PTR(mptr);
|
||||
|
||||
if (mptr->common.fn_flags & ZEND_ACC_STATIC) {
|
||||
zend_create_closure(return_value, mptr, mptr->common.scope, NULL TSRMLS_CC);
|
||||
} else {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instanceof_function(Z_OBJCE_P(obj), mptr->common.scope TSRMLS_CC)) {
|
||||
_DO_THROW("Given object is not an instance of the class this method was declared in");
|
||||
/* Returns from this function */
|
||||
}
|
||||
|
||||
zend_create_closure(return_value, mptr, mptr->common.scope, obj TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public mixed ReflectionMethod::invoke(mixed object, mixed* args)
|
||||
Invokes the method. */
|
||||
ZEND_METHOD(reflection_method, invoke)
|
||||
|
@ -4641,6 +4684,7 @@ static const zend_function_entry reflection_function_functions[] = {
|
|||
ZEND_ME(reflection_function, __toString, NULL, 0)
|
||||
ZEND_ME(reflection_function, export, arginfo_reflection_function_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(reflection_function, isDisabled, NULL, 0)
|
||||
ZEND_ME(reflection_function, getClosure, NULL, 0)
|
||||
ZEND_ME(reflection_function, invoke, arginfo_reflection_function_invoke, 0)
|
||||
ZEND_ME(reflection_function, invokeArgs, arginfo_reflection_function_invokeArgs, 0)
|
||||
{NULL, NULL, NULL}
|
||||
|
@ -4684,6 +4728,7 @@ static const zend_function_entry reflection_method_functions[] = {
|
|||
ZEND_ME(reflection_method, isConstructor, NULL, 0)
|
||||
ZEND_ME(reflection_method, isDestructor, NULL, 0)
|
||||
ZEND_ME(reflection_method, getModifiers, NULL, 0)
|
||||
ZEND_ME(reflection_method, getClosure, NULL, 0)
|
||||
ZEND_ME(reflection_method, invoke, arginfo_reflection_method_invoke, 0)
|
||||
ZEND_ME(reflection_method, invokeArgs, arginfo_reflection_method_invokeArgs, 0)
|
||||
ZEND_ME(reflection_method, getDeclaringClass, NULL, 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue