From 8d2e0a7e0f91c5906550d81249569cfd822c9598 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 8 Jul 2008 07:05:04 +0000 Subject: [PATCH] Added closures support --- NEWS | 1 + Zend/Makefile.am | 2 +- Zend/Zend.dsp | 4 + Zend/ZendTS.dsp | 4 + Zend/tests/closure_001.phpt | 34 ++++ Zend/tests/closure_002.phpt | 33 ++++ Zend/tests/closure_003.phpt | 37 ++++ Zend/tests/closure_004.phpt | 39 ++++ Zend/tests/closure_005.phpt | 78 ++++++++ Zend/tests/closure_006.phpt | 23 +++ Zend/tests/closure_007.phpt | 42 +++++ Zend/tests/closure_008.phpt | 26 +++ Zend/tests/closure_009.phpt | 35 ++++ Zend/tests/closure_010.phpt | 22 +++ Zend/tests/closure_011.phpt | 18 ++ Zend/tests/closure_012.phpt | 28 +++ Zend/zend.h | 2 + Zend/zend_API.c | 10 + Zend/zend_closures.c | 315 ++++++++++++++++++++++++++++++++ Zend/zend_closures.h | 42 +++++ Zend/zend_compile.c | 62 ++++++- Zend/zend_compile.h | 6 +- Zend/zend_default_classes.c | 2 + Zend/zend_execute.c | 1 + Zend/zend_execute_API.c | 9 +- Zend/zend_language_parser.y | 28 ++- Zend/zend_vm_def.h | 24 +++ Zend/zend_vm_execute.h | 76 ++++++++ Zend/zend_vm_opcodes.h | 1 + configure.in | 2 +- ext/pcre/php_pcre.c | 2 +- ext/reflection/php_reflection.c | 45 +++++ 32 files changed, 1040 insertions(+), 13 deletions(-) create mode 100644 Zend/tests/closure_001.phpt create mode 100644 Zend/tests/closure_002.phpt create mode 100644 Zend/tests/closure_003.phpt create mode 100644 Zend/tests/closure_004.phpt create mode 100644 Zend/tests/closure_005.phpt create mode 100644 Zend/tests/closure_006.phpt create mode 100644 Zend/tests/closure_007.phpt create mode 100644 Zend/tests/closure_008.phpt create mode 100644 Zend/tests/closure_009.phpt create mode 100644 Zend/tests/closure_010.phpt create mode 100644 Zend/tests/closure_011.phpt create mode 100644 Zend/tests/closure_012.phpt create mode 100644 Zend/zend_closures.c create mode 100644 Zend/zend_closures.h diff --git a/NEWS b/NEWS index 639c105323..2c6800b191 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 20??, PHP 6.0 - Unicode support. (Andrei, Dmitry, et al) +- Closures (Christian Seiler, Dmitry) - Changed dl() to be disabled by default. Enabled only when explicitly registered by the SAPI layer. Enabled only with CLI, CGI and EMBED. (Dmitry) diff --git a/Zend/Makefile.am b/Zend/Makefile.am index 464cbe9881..3283effca3 100644 --- a/Zend/Makefile.am +++ b/Zend/Makefile.am @@ -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_strtol.c + zend_strtod.c zend_strtod.c zend_strtol.c zend_closures.c libZend_la_LDFLAGS = libZend_la_LIBADD = @ZEND_EXTRA_LIBS@ diff --git a/Zend/Zend.dsp b/Zend/Zend.dsp index ef6dbc8b2d..6d3f404a71 100644 --- a/Zend/Zend.dsp +++ b/Zend/Zend.dsp @@ -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 diff --git a/Zend/ZendTS.dsp b/Zend/ZendTS.dsp index a2bc820ad0..92d2be0935 100644 --- a/Zend/ZendTS.dsp +++ b/Zend/ZendTS.dsp @@ -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 diff --git a/Zend/tests/closure_001.phpt b/Zend/tests/closure_001.phpt new file mode 100644 index 0000000000..03934bbeb6 --- /dev/null +++ b/Zend/tests/closure_001.phpt @@ -0,0 +1,34 @@ +--TEST-- +Closure 001: Lambda without lexical variables +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +Hello World! +Hello Universe! +Hello World! +Hello Universe! +Done diff --git a/Zend/tests/closure_002.phpt b/Zend/tests/closure_002.phpt new file mode 100644 index 0000000000..a9e8c14ad1 --- /dev/null +++ b/Zend/tests/closure_002.phpt @@ -0,0 +1,33 @@ +--TEST-- +Closure 002: Lambda with lexical variables (global scope) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +4 +4 +4 +5 +Done diff --git a/Zend/tests/closure_003.phpt b/Zend/tests/closure_003.phpt new file mode 100644 index 0000000000..b191f35e9c --- /dev/null +++ b/Zend/tests/closure_003.phpt @@ -0,0 +1,37 @@ +--TEST-- +Closure 003: Lambda with lexical variables (local scope) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +4 +4 +4 +5 +Done diff --git a/Zend/tests/closure_004.phpt b/Zend/tests/closure_004.phpt new file mode 100644 index 0000000000..40e0d32068 --- /dev/null +++ b/Zend/tests/closure_004.phpt @@ -0,0 +1,39 @@ +--TEST-- +Closure 004: Lambda with lexical variables (scope lifetime) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +4 +4 +4 +5 +Done diff --git a/Zend/tests/closure_005.phpt b/Zend/tests/closure_005.phpt new file mode 100644 index 0000000000..ab4f3a35cf --- /dev/null +++ b/Zend/tests/closure_005.phpt @@ -0,0 +1,78 @@ +--TEST-- +Closure 005: Lambda inside class, lifetime of $this +--SKIPIF-- + +--FILE-- +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 diff --git a/Zend/tests/closure_006.phpt b/Zend/tests/closure_006.phpt new file mode 100644 index 0000000000..7da96703a8 --- /dev/null +++ b/Zend/tests/closure_006.phpt @@ -0,0 +1,23 @@ +--TEST-- +Closure 006: Nested lambdas +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Hello World: 2! +Done diff --git a/Zend/tests/closure_007.phpt b/Zend/tests/closure_007.phpt new file mode 100644 index 0000000000..cb650f81ed --- /dev/null +++ b/Zend/tests/closure_007.phpt @@ -0,0 +1,42 @@ +--TEST-- +Closure 007: Nested lambdas in classes +--SKIPIF-- + +--FILE-- +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 diff --git a/Zend/tests/closure_008.phpt b/Zend/tests/closure_008.phpt new file mode 100644 index 0000000000..3ba74aabf0 --- /dev/null +++ b/Zend/tests/closure_008.phpt @@ -0,0 +1,26 @@ +--TEST-- +Closure 008: Use in preg_replace() +--SKIPIF-- + +--FILE-- + +--EXPECT-- +1 2 3 +1  2  3 +1   2   3 +Done diff --git a/Zend/tests/closure_009.phpt b/Zend/tests/closure_009.phpt new file mode 100644 index 0000000000..3c9a820e55 --- /dev/null +++ b/Zend/tests/closure_009.phpt @@ -0,0 +1,35 @@ +--TEST-- +Closure 009: Use in preg_replace() +--SKIPIF-- + +--FILE-- + +--EXPECT-- +1:1:1 +2:2:1 +3:3:1 +4:1:1 +5:2:1:1 +6:3:2:1:1 diff --git a/Zend/tests/closure_010.phpt b/Zend/tests/closure_010.phpt new file mode 100644 index 0000000000..da0998e9fb --- /dev/null +++ b/Zend/tests/closure_010.phpt @@ -0,0 +1,22 @@ +--TEST-- +Closure 010: Closure calls itself +--SKIPIF-- + +--FILE-- + +--EXPECT-- +3 +2 +1 +0 diff --git a/Zend/tests/closure_011.phpt b/Zend/tests/closure_011.phpt new file mode 100644 index 0000000000..3cd900f825 --- /dev/null +++ b/Zend/tests/closure_011.phpt @@ -0,0 +1,18 @@ +--TEST-- +Closure 011: Lexical copies not static in closure +--SKIPIF-- + +--FILE-- + +--EXPECT-- +2 diff --git a/Zend/tests/closure_012.phpt b/Zend/tests/closure_012.phpt new file mode 100644 index 0000000000..fd97e947a6 --- /dev/null +++ b/Zend/tests/closure_012.phpt @@ -0,0 +1,28 @@ +--TEST-- +Closure 012: Undefined lexical variables +--SKIPIF-- + +--FILE-- + +--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) + diff --git a/Zend/zend.h b/Zend/zend.h index 1191ef9489..8bb845a866 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -561,6 +561,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) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 570d8f7bc0..4ea1abbf1e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -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 @@ -3093,6 +3094,15 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *c } return 0; + case IS_OBJECT: + if (zend_get_closure(callable, ce_ptr, fptr_ptr, NULL, zobj_ptr_ptr TSRMLS_CC) == SUCCESS) { + if (callable_name) { + ZVAL_ZSTR(callable_name, UG(unicode) ? IS_UNICODE : IS_STRING, (*fptr_ptr)->common.function_name, 1); + } + return 1; + } + /* break missing intentionally */ + default: if (callable_name) { *callable_name = *callable; diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c new file mode 100644 index 0000000000..3a7f48e84e --- /dev/null +++ b/Zend/zend_closures.c @@ -0,0 +1,315 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +/* $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" + +typedef struct _zend_closure { + zend_object std; + zend_function func; + zval *this_ptr; +} 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 (!return_value_ptr) { + return_value_ptr = &closure_result_ptr; + } + + if (call_user_function_ex(CG(function_table), NULL, this_ptr, return_value_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) { + efree(arguments); + RETURN_FALSE; + } + + efree(arguments); + if (closure_result_ptr) { + RETVAL_ZVAL(closure_result_ptr, 1, 1); + } +} +/* }}} */ + +const static zend_function_entry closure_functions[] = { /* {{{ */ + ZEND_ME(Closure, __invoke, NULL, 0) + {NULL, NULL, NULL} +}; +/* }}} */ + +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, void *extra TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce; + + switch (type) { + case IS_STRING: + case IS_UNICODE: + INIT_PZVAL(writeobj); + ZVAL_ASCII_STRINGL(writeobj, "Closure object", sizeof("Closure object")-1, ZSTR_DUPLICATE); + return SUCCESS; + default: + ce = Z_OBJCE_P(readobj); + zend_error(E_NOTICE, "Object of class %v 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 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); + } + + 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", closure_functions); + 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.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; + TSRMLS_FETCH(); + + 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_u_hash_quick_find(EG(active_symbol_table), key->type, key->arKey, key->nKeyLength, key->h, (void **) &p) == FAILURE) { + if (is_ref) { + zval *tmp; + + ALLOC_INIT_ZVAL(tmp); + Z_SET_ISREF_P(tmp); + zend_u_hash_quick_add(EG(active_symbol_table), key->type, 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_u_hash_quick_add(target, key->type, 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_u_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0, UG(unicode)); + 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) /* {{{ */ +{ + zstr key; + zend_uchar utype = UG(unicode)?IS_UNICODE:IS_STRING; + + if (utype == IS_UNICODE) { + key.u = USTR_MAKE("__invoke"); + } else { + key.s = "__invoke"; + } + + 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; + } + if (utype == IS_UNICODE) { + efree(key.u); + } + return SUCCESS; + } else if (zend_u_hash_find(&ce->function_table, utype, key, sizeof("__invoke"), (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; + } + } + if (utype == IS_UNICODE) { + efree(key.u); + } + return SUCCESS; + } + } + if (utype == IS_UNICODE) { + efree(key.u); + } + return FAILURE; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h new file mode 100644 index 0000000000..c3a3b87095 --- /dev/null +++ b/Zend/zend_closures.h @@ -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 | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +/* $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: + */ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7187f097f4..dd2d49ba79 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1474,6 +1474,33 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } /* }}} */ +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_ASCII_STRING(&function_name.u.constant, "lambda", ZSTR_DUPLICATE); + + 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_u_hash_func(Z_TYPE(current_op->op1.u.constant), Z_UNIVAL(current_op->op1.u.constant), Z_UNILEN(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); @@ -4374,13 +4401,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) { @@ -4388,13 +4415,40 @@ 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); - CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; + 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_UNILEN(varname->u.constant) == sizeof("this") - 1 && + ZEND_U_EQUAL(UG(unicode)?IS_UNICODE:IS_STRING, Z_UNIVAL(varname->u.constant), Z_UNILEN(varname->u.constant), "this", sizeof("this")-1)) { + 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; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8404bc6d33..0f8d6a8208 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -151,6 +151,7 @@ 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); @@ -438,6 +439,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); @@ -640,7 +644,7 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_FETCH_STATIC_MEMBER 3 #define ZEND_FETCH_GLOBAL_LOCK 4 #define ZEND_FETCH_AUTO_GLOBAL 5 - +#define ZEND_FETCH_LEXICAL 6 /* class fetches */ #define ZEND_FETCH_CLASS_DEFAULT 0 diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 54b2de62dc..0479aee8af 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -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_default_exception(TSRMLS_C); zend_register_unicode_exceptions(TSRMLS_C); zend_register_iterator_wrapper(TSRMLS_C); + zend_register_closure_ce(TSRMLS_C); } /* }}} */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e7cf5962c8..8c509bb735 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.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" #include "zend_unicode.h" diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index b33f63857a..0f3bc816d7 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -29,6 +29,7 @@ #include "zend_ptr_stack.h" #include "zend_constants.h" #include "zend_extensions.h" +#include "zend_closures.h" #include "zend_exceptions.h" #include "zend_vm.h" #ifdef HAVE_SYS_TIME_H @@ -889,7 +890,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 && Z_TYPE_P(fci->function_name) != IS_UNICODE ) { return FAILURE; @@ -1035,6 +1040,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) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c1cfdd4e97..1993f543f2 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -304,7 +304,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); } ; @@ -512,8 +512,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); } ; @@ -647,10 +647,30 @@ expr_without_variable: | T_ARRAY '(' array_pair_list ')' { $$ = $3; } | '`' { CG(literal_type) = UG(unicode)?IS_UNICODE:IS_STRING; } encaps_list '`' { zend_do_shell_exec(&$$, &$3 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); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7a56fd6cf6..d468bdb97a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2086,6 +2086,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 && Z_TYPE_P(function_name) != IS_UNICODE) { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -4533,4 +4542,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_u_hash_quick_find(EG(function_table), UG(unicode)?IS_UNICODE:IS_STRING, Z_UNIVAL(opline->op1.u.constant), Z_UNILEN(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) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0c0c4cf1ff..d43317a1d1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -756,6 +756,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 && Z_TYPE_P(function_name) != IS_UNICODE) { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -946,6 +955,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_HA } 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 && Z_TYPE_P(function_name) != IS_UNICODE) { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -1024,6 +1042,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_HA } 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 && Z_TYPE_P(function_name) != IS_UNICODE) { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -1131,6 +1158,15 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HAN } 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 && Z_TYPE_P(function_name) != IS_UNICODE) { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -2960,6 +2996,21 @@ static int ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE 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_u_hash_quick_find(EG(function_table), UG(unicode)?IS_UNICODE:IS_STRING, Z_UNIVAL(opline->op1.u.constant), Z_UNILEN(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); @@ -34712,6 +34763,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; diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 72b15286a1..acac7309f6 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -153,3 +153,4 @@ #define ZEND_USER_OPCODE 150 #define ZEND_U_NORMALIZE 151 #define ZEND_JMP_SET 152 +#define ZEND_DECLARE_LAMBDA_FUNCTION 153 diff --git a/configure.in b/configure.in index c5992426fb..3ebbd03ea2 100644 --- a/configure.in +++ b/configure.in @@ -1334,7 +1334,7 @@ PHP_ADD_SOURCES(Zend, \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_ts_hash.c zend_stream.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c \ - zend_strtol.c zend_gc.c) + zend_strtol.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 \ diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 2d0656ab30..2e7638ed51 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -1460,7 +1460,7 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_callabl } if (is_callable_replace) { - if (Z_TYPE_P(replace) != IS_ARRAY) { + if (Z_TYPE_P(replace) != IS_ARRAY && Z_TYPE_P(replace) != IS_OBJECT) { convert_to_text(replace); } if (!zend_is_callable(replace, 0, &callback_name)) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ad027ae125..0549ad4dd1 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -38,6 +38,7 @@ #include "zend_constants.h" #include "zend_ini.h" #include "zend_interfaces.h" +#include "zend_closures.h" /* Class entry pointers */ PHPAPI zend_class_entry *reflector_ptr; @@ -1586,6 +1587,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) U Invokes the function */ ZEND_METHOD(reflection_function, invoke) @@ -2333,6 +2348,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) U Invokes the method. */ ZEND_METHOD(reflection_method, invoke) @@ -4781,6 +4824,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} @@ -4824,6 +4868,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) -- 2.40.0