diff --git a/Zend/tests/closure_composition/auto-promote-closure.phpt b/Zend/tests/closure_composition/auto-promote-closure.phpt new file mode 100644 index 000000000000..0d6fee18c4c9 --- /dev/null +++ b/Zend/tests/closure_composition/auto-promote-closure.phpt @@ -0,0 +1,9 @@ +--TEST-- +Closures get auto promoted to a Composed Closure. +--FILE-- + "<$x>"); +var_dump($cb("Hello World")); +--EXPECT-- +string(13) "" diff --git a/Zend/tests/closure_composition/compose-operator.phpt b/Zend/tests/closure_composition/compose-operator.phpt new file mode 100644 index 000000000000..63d606ccea7b --- /dev/null +++ b/Zend/tests/closure_composition/compose-operator.phpt @@ -0,0 +1,12 @@ +--TEST-- +Composed callable using composition. +--FILE-- +prepend(fn($x) => "$x World"); +var_dump($cb("Hello")); +$cb->append('strrev'); +var_dump($cb("Hello")); +$cb->insert(ucfirst(...), 2); +var_dump($cb("Hello")); +--EXPECT-- +string(5) "hello" +string(11) "hello world" +string(11) "dlrow olleh" +string(11) "dlrow olleH" diff --git a/Zend/tests/closure_composition/invokable.phpt b/Zend/tests/closure_composition/invokable.phpt new file mode 100644 index 000000000000..453a2384238c --- /dev/null +++ b/Zend/tests/closure_composition/invokable.phpt @@ -0,0 +1,27 @@ +--TEST-- +Invokable objects may be composed +--FILE-- +x * $y; + } +} + +function double(int $x): int { + return $x * 2; +} + +$cb1 = double(...) + new Times(3); +var_dump($cb1(4)); + +$cb2 = new Times(3) + double(...); +var_dump($cb2(4)); + +--EXPECT-- +int(24) +int(24) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 5777e1a34a2b..6a4a2ca29dda 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -21,6 +21,7 @@ #include "zend.h" #include "zend_API.h" #include "zend_closures.h" +#include "zend_composed_callable.h" #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_objects.h" @@ -705,6 +706,14 @@ ZEND_COLD ZEND_METHOD(Closure, __construct) } /* }}} */ +static zend_result zend_closure_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2) { + if (opcode != ZEND_ADD) { + return FAILURE; + } + + return zend_composed_callable_new_from_pair(result, op1, op2); +} + void zend_register_closure_ce(void) /* {{{ */ { zend_ce_closure = register_class_Closure(); @@ -720,6 +729,7 @@ void zend_register_closure_ce(void) /* {{{ */ closure_handlers.get_debug_info = zend_closure_get_debug_info; closure_handlers.get_closure = zend_closure_get_closure; closure_handlers.get_gc = zend_closure_get_gc; + closure_handlers.do_operation = zend_closure_do_operation; } /* }}} */ diff --git a/Zend/zend_composed_callable.c b/Zend/zend_composed_callable.c new file mode 100644 index 000000000000..46da5f82e7a4 --- /dev/null +++ b/Zend/zend_composed_callable.c @@ -0,0 +1,373 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 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: Sara Golemon | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "zend_API.h" +#include "zend_composed_callable.h" +#include "zend_exceptions.h" + +#define ZEND_COMPOSED_CALLABLE_INITIAL_CAPACITY 8 + +ZEND_API zend_class_entry *zend_ce_composed_callable; +static zend_object_handlers zend_composed_callable_handlers; + +typedef struct { + HashTable callables; + zend_object std; +} zend_composed_callable; + +static zend_fcall_info_cache* zend_composed_callable_fcc_from_zval(zval* pzv) { + ZEND_ASSERT(Z_TYPE_P(pzv) == IS_PTR); + return Z_PTR_P(pzv); +} + +static zend_object* zend_composed_callable_to_zend_object(zend_composed_callable* objval) { + return ((zend_object*)(objval + 1)) - 1; +} + +static zend_composed_callable* zend_composed_callable_from_zend_object(zend_object* zobj) { + return ((zend_composed_callable*)(zobj + 1)) - 1; +} + +static zend_result zend_composed_callable_append1(HashTable *callables, zval *callable) { + zend_fcall_info_cache *fcc = ecalloc(1, sizeof(zend_fcall_info_cache)); + char *error = NULL; + zval ptr; + + if (!zend_is_callable_ex(callable, NULL, 0, NULL, fcc, &error)) { + if (error) { + zend_throw_exception_ex(zend_ce_value_error, 0, "Argument not valid callback: %s", error); + efree(error); + } else { + zend_throw_exception(zend_ce_value_error, "Unable to initialize callback", 0); + } + efree(fcc); + return FAILURE; + } + + ZVAL_PTR(&ptr, fcc); + zend_fcc_addref(fcc); + zend_hash_next_index_insert(callables, &ptr); + + return SUCCESS; +} + +static zend_result zend_composed_callable_splice(HashTable *dst, HashTable *src, zend_long pos, zval *insert); +static zend_result zend_composed_callable_append(HashTable *callables, zval *callable) { + if (Z_TYPE_P(callable) == IS_ARRAY) { + zval *fn; + + ZEND_HASH_FOREACH_VAL(Z_ARR_P(callable), fn) { + if (zend_composed_callable_append1(callables, fn) == FAILURE) { + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + + return SUCCESS; + } else if ((Z_TYPE_P(callable) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(callable), zend_ce_composed_callable)) { + HashTable *src_callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(callable))->callables); + return zend_composed_callable_splice(callables, src_callables, 0, NULL); + } else { + return zend_composed_callable_append1(callables, callable); + } +} + +ZEND_METHOD(ComposedCallable, __construct) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + zval *callable; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_composed_callable_append(callables, callable) == FAILURE) { + RETURN_THROWS(); + } +} + +ZEND_METHOD(ComposedCallable, __invoke) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + zval *argv, *callable; + uint32_t argc = 0, i; + + if (zend_hash_num_elements(callables) < 1) { + zend_throw_exception(zend_ce_value_error, "Attempt to invoke an empty ComposedCallable", 0); + RETURN_THROWS(); + } + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "*", &argv, &argc) == FAILURE) { + RETURN_THROWS(); + } + + /* Prevent destruction of initial args during intermediate forwarding. */ + for (i = 0; i < argc; ++i) { + Z_TRY_ADDREF(argv[i]); + } + + ZEND_HASH_FOREACH_VAL(callables, callable) { + zend_fcall_info_cache *fcc = zend_composed_callable_fcc_from_zval(callable); + zval retval; + + zend_call_known_fcc(fcc, &retval, argc, argv, NULL); + + /* Clean up the prior call vars. */ + for (i = 0; i < argc; ++i) { + zval_ptr_dtor(&(argv[i])); + } + + /* Forward retval to next invocation. */ + argv[0] = retval; + argc = 1; + } ZEND_HASH_FOREACH_END(); + + /* The check at the top of this method ensures we call at least one callable, + * and every callable has precisely one return value (even if it's NULL). + */ + ZEND_ASSERT(argc == 1); + RETURN_ZVAL(&(argv[0]), 1, 0); +} + +static zend_result zend_composed_callable_splice(HashTable *dst, HashTable *src, zend_long pos, zval *insert) { + zval *src_callable; + ZEND_HASH_FOREACH_VAL(src, src_callable) { + if (insert && pos-- == 0) { + if (zend_composed_callable_append(dst, insert) == FAILURE) { + return FAILURE; + } + insert = NULL; + /* fallthrough to continue splice */ + } + + { + zend_fcall_info_cache *src_fcc = zend_composed_callable_fcc_from_zval(src_callable); + zend_fcall_info_cache *dst_fcc = ecalloc(1, sizeof(zend_fcall_info_cache)); + zval dst_callable; + zend_fcc_dup(dst_fcc, src_fcc); + ZVAL_PTR(&dst_callable, dst_fcc); + zend_hash_next_index_insert(dst, &dst_callable); + } + } ZEND_HASH_FOREACH_END(); + + /* append splice */ + if (insert && (zend_composed_callable_append(dst, insert) == FAILURE)) { + return FAILURE; + } + + return SUCCESS; +} + +static void zend_composed_callable_fcc_dtor(zval *ptr) { + zend_fcall_info_cache *fcc = zend_composed_callable_fcc_from_zval(ptr); + zend_fcc_dtor(fcc); + efree(fcc); +} + +static zend_result zend_composed_callable_do_insert(HashTable *callables, zend_long pos, zval *callable) { + zend_long num_callables = zend_hash_num_elements(callables); + + if (pos < 0) { + /* For "-2" meaning "right before the last one" kind of semantics */ + pos += num_callables; + } + + if (pos < 0) { + pos = 0; + /* Maybe error? */ + } + + if (pos > num_callables) { + pos = num_callables; + /* Maybe error? */ + } + + if (pos == num_callables) { + /* Quick method. This is just append() with more steps. */ + return zend_composed_callable_append(callables, callable); + } + + /* Slow path, rebuild the hashtable :( */ + HashTable newtable; + zend_hash_init(&newtable, ZEND_COMPOSED_CALLABLE_INITIAL_CAPACITY, NULL, zend_composed_callable_fcc_dtor, 0); + if (zend_composed_callable_splice(&newtable, callables, pos, callable) == FAILURE) { + zend_hash_destroy(&newtable); + return FAILURE; + } + + zend_hash_destroy(callables); + *callables = newtable; + return SUCCESS; +} + +ZEND_METHOD(ComposedCallable, insert) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + zval *callable; + zend_long pos; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zl", &callable, &pos) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_composed_callable_do_insert(callables, pos, callable) == FAILURE) { + RETURN_THROWS(); + } + + RETURN_ZVAL(getThis(), 1, 0); +} + +ZEND_METHOD(ComposedCallable, prepend) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + zval *callable; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_composed_callable_do_insert(callables, 0, callable) == FAILURE) { + RETURN_THROWS(); + } + + RETURN_ZVAL(getThis(), 1, 0); +} + +ZEND_METHOD(ComposedCallable, append) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + zval *callable; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_composed_callable_append(callables, callable) == FAILURE) { + RETURN_THROWS(); + } + + RETURN_ZVAL(getThis(), 1, 0); +} + +ZEND_API zend_result zend_composed_callable_new_from_pair(zval *result, zval *callable1, zval *callable2) { + zend_object *ret = zend_ce_composed_callable->create_object(zend_ce_composed_callable); + HashTable *callables = &(zend_composed_callable_from_zend_object(ret)->callables); + + if ((zend_composed_callable_append(callables, callable1) == FAILURE) || + (zend_composed_callable_append(callables, callable2) == FAILURE)) { + return FAILURE; + } + + ZVAL_OBJ(result, ret); + return SUCCESS; +} + +static zend_result zend_composed_callable_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2) { + bool append; + + if (opcode != ZEND_ADD) { + return FAILURE; + } + + if ((Z_TYPE_P(op1) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(op1), zend_ce_composed_callable)) { + /* composed + callableOrComposed + * Clone op1's invocations into new result, + * then do standard append on op2, whatever it is. + */ + append = true; + } else { + ZEND_ASSERT((Z_TYPE_P(op2) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(op2), zend_ce_composed_callable)); + /* callable + composed + * Start with just the initial callable, then clone in rhs' entries. + */ + append = false; + } + + { + zend_object *composed_obj = append ? Z_OBJ_P(op1) : Z_OBJ_P(op2); + HashTable *src_callables = &(zend_composed_callable_from_zend_object(composed_obj)->callables); + uint32_t pos = append ? zend_hash_num_elements(src_callables) : 0; + + zend_object *ret = composed_obj->ce->create_object(composed_obj->ce); + HashTable *dst_callables = &(zend_composed_callable_from_zend_object(ret)->callables); + + zval *insert_obj = append ? op2 : op1; + + ZVAL_OBJ(result, ret); + if (zend_composed_callable_splice(dst_callables, src_callables, pos, insert_obj) == FAILURE) { + zval_ptr_dtor(result); + ZVAL_UNDEF(result); + return FAILURE; + } + + return SUCCESS; + } +} + +ZEND_METHOD(ComposedCallable, __debugInfo) { + HashTable *callables = &(zend_composed_callable_from_zend_object(Z_OBJ_P(getThis()))->callables); + + if (zend_parse_parameters_none_throw() == FAILURE) { + RETURN_THROWS(); + } + + array_init(return_value); + { + zval callables_names, *callable; + array_init(&callables_names); + ZEND_HASH_FOREACH_VAL(callables, callable) { + zend_string *name = zend_composed_callable_fcc_from_zval(callable)->function_handler->common.function_name; + add_next_index_str(&callables_names, name); + } ZEND_HASH_FOREACH_END(); + add_assoc_zval(return_value, "callables", &callables_names); + } +} + +static zend_object *zend_composed_callable_create(zend_class_entry *ce) { + zend_composed_callable *objval = ecalloc(1, sizeof(zend_composed_callable) + zend_object_properties_size(ce)); + zend_object* ret = zend_composed_callable_to_zend_object(objval); + zend_object_std_init(ret, ce); + zend_hash_init(&objval->callables, ZEND_COMPOSED_CALLABLE_INITIAL_CAPACITY, NULL, zend_composed_callable_fcc_dtor, 0); + ret->handlers = &zend_composed_callable_handlers; + return ret; +} + +static zend_object* zend_composed_callable_clone(zend_object* zsrc) { + zend_object *zdst = zsrc->ce->create_object(zsrc->ce); + zend_objects_clone_members(zdst, zsrc); + + zend_composed_callable *src = zend_composed_callable_from_zend_object(zsrc); + zend_composed_callable *dst = zend_composed_callable_from_zend_object(zdst); + + zend_composed_callable_splice(&dst->callables, &src->callables, 0, NULL); + return zdst; +} + +static void zend_composed_callable_free(zend_object *zobj) { + zend_composed_callable *obj = zend_composed_callable_from_zend_object(zobj); + zend_object_std_dtor(zobj); + zend_hash_destroy(&obj->callables); + efree(obj); +} + +void zend_register_composed_callable(zend_class_entry *ce) { + zend_ce_composed_callable = ce; + ce->create_object = zend_composed_callable_create; + + memcpy(&zend_composed_callable_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + zend_composed_callable_handlers.offset = XtOffsetOf(zend_composed_callable, std); + zend_composed_callable_handlers.clone_obj = zend_composed_callable_clone; + zend_composed_callable_handlers.free_obj = zend_composed_callable_free; + + zend_composed_callable_handlers.do_operation = zend_composed_callable_do_operation; +} diff --git a/Zend/zend_composed_callable.h b/Zend/zend_composed_callable.h new file mode 100644 index 000000000000..8e36cb26eebd --- /dev/null +++ b/Zend/zend_composed_callable.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 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: Sara Golemon | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_COMPOSED_CALLABLE_H +#define ZEND_COMPOSED_CALLABLE_H + +#include "zend.h" +#include "zend_API.h" + +BEGIN_EXTERN_C() + +extern ZEND_API zend_class_entry *zend_ce_composed_callable; + +ZEND_API zend_result zend_composed_callable_new_from_pair(zval *result, zval *callable1, zval *callable2); + +void zend_register_composed_callable(zend_class_entry *ce); + +END_EXTERN_C() + +#endif /* ZEND_COMPOSED_CALLABLE_H */ diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index ce9cc00fdfb9..d1bf51f1bb93 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -18,6 +18,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_composed_callable.h" #include "zend_interfaces.h" #include "zend_exceptions.h" #include "zend_interfaces_arginfo.h" @@ -676,5 +677,7 @@ ZEND_API void zend_register_interfaces(void) sizeof(zend_object_handlers)); zend_internal_iterator_handlers.clone_obj = NULL; zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free; + + zend_register_composed_callable(register_class_ComposedCallable()); } /* }}} */ diff --git a/Zend/zend_interfaces.stub.php b/Zend/zend_interfaces.stub.php index 2247205e4697..a3bd39ef41db 100644 --- a/Zend/zend_interfaces.stub.php +++ b/Zend/zend_interfaces.stub.php @@ -83,3 +83,12 @@ public function valid(): bool {} public function rewind(): void {} } + +final class ComposedCallable { + public function __construct(array|callable|\ComposedCallable $callable) {} + public function __invoke(mixed ...$args): mixed {} + public function __debugInfo(): array {} + public function append(callable|\ComposedCallable $callable): \ComposedCallable {} + public function insert(callable|\ComposedCallable $callable, int $idx): \ComposedCallable {} + public function prepend(callable|\ComposedCallable $callable): \ComposedCallable {} +} diff --git a/Zend/zend_interfaces_arginfo.h b/Zend/zend_interfaces_arginfo.h index 8a90166b2d80..70a5445628c6 100644 --- a/Zend/zend_interfaces_arginfo.h +++ b/Zend/zend_interfaces_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a9c915c11e5989d8c7cf2d704ada09ca765670c3 */ + * Stub hash: 2dcce186cc58725fdb4127d15b29cd9bcb0968af */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_IteratorAggregate_getIterator, 0, 0, Traversable, 0) ZEND_END_ARG_INFO() @@ -62,12 +62,40 @@ ZEND_END_ARG_INFO() #define arginfo_class_InternalIterator_rewind arginfo_class_InternalIterator_next +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ComposedCallable___construct, 0, 0, 1) + ZEND_ARG_OBJ_TYPE_MASK(0, callable, ComposedCallable, MAY_BE_ARRAY|MAY_BE_CALLABLE, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ComposedCallable___invoke, 0, 0, IS_MIXED, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ComposedCallable___debugInfo, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ComposedCallable_append, 0, 1, ComposedCallable, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, callable, ComposedCallable, MAY_BE_CALLABLE, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ComposedCallable_insert, 0, 2, ComposedCallable, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, callable, ComposedCallable, MAY_BE_CALLABLE, NULL) + ZEND_ARG_TYPE_INFO(0, idx, IS_LONG, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ComposedCallable_prepend arginfo_class_ComposedCallable_append + ZEND_METHOD(InternalIterator, __construct); ZEND_METHOD(InternalIterator, current); ZEND_METHOD(InternalIterator, key); ZEND_METHOD(InternalIterator, next); ZEND_METHOD(InternalIterator, valid); ZEND_METHOD(InternalIterator, rewind); +ZEND_METHOD(ComposedCallable, __construct); +ZEND_METHOD(ComposedCallable, __invoke); +ZEND_METHOD(ComposedCallable, __debugInfo); +ZEND_METHOD(ComposedCallable, append); +ZEND_METHOD(ComposedCallable, insert); +ZEND_METHOD(ComposedCallable, prepend); static const zend_function_entry class_IteratorAggregate_methods[] = { ZEND_RAW_FENTRY("getIterator", NULL, arginfo_class_IteratorAggregate_getIterator, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) @@ -117,6 +145,16 @@ static const zend_function_entry class_InternalIterator_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ComposedCallable_methods[] = { + ZEND_ME(ComposedCallable, __construct, arginfo_class_ComposedCallable___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ComposedCallable, __invoke, arginfo_class_ComposedCallable___invoke, ZEND_ACC_PUBLIC) + ZEND_ME(ComposedCallable, __debugInfo, arginfo_class_ComposedCallable___debugInfo, ZEND_ACC_PUBLIC) + ZEND_ME(ComposedCallable, append, arginfo_class_ComposedCallable_append, ZEND_ACC_PUBLIC) + ZEND_ME(ComposedCallable, insert, arginfo_class_ComposedCallable_insert, ZEND_ACC_PUBLIC) + ZEND_ME(ComposedCallable, prepend, arginfo_class_ComposedCallable_prepend, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Traversable(void) { zend_class_entry ce, *class_entry; @@ -199,3 +237,13 @@ static zend_class_entry *register_class_InternalIterator(zend_class_entry *class return class_entry; } + +static zend_class_entry *register_class_ComposedCallable(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ComposedCallable", class_ComposedCallable_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + return class_entry; +} diff --git a/configure.ac b/configure.ac index e4bd8162a2eb..37e1a93f1ec4 100644 --- a/configure.ac +++ b/configure.ac @@ -1737,6 +1737,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_call_stack.c zend_closures.c zend_compile.c + zend_composed_callable.c zend_constants.c zend_cpuinfo.c zend_default_classes.c