From aa3c7aa43850ab778bbde28d11c6a4f38a05ab8c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 14 May 2015 20:10:35 +0200 Subject: [PATCH] Add ReflectionGenerator class --- ext/reflection/php_reflection.c | 197 ++++++++++++++++++ .../tests/ReflectionGenerator_basic.phpt | 87 ++++++++ .../ReflectionGenerator_in_Generator.phpt | 65 ++++++ 3 files changed, 349 insertions(+) create mode 100644 ext/reflection/tests/ReflectionGenerator_basic.phpt create mode 100644 ext/reflection/tests/ReflectionGenerator_in_Generator.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index f7dd5531d0..65febf4a53 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -39,7 +39,9 @@ #include "zend_ini.h" #include "zend_interfaces.h" #include "zend_closures.h" +#include "zend_generators.h" #include "zend_extensions.h" +#include "zend_builtin_functions.h" #define reflection_update_property(object, name, value) do { \ zval member; \ @@ -55,6 +57,7 @@ PHPAPI zend_class_entry *reflection_exception_ptr; PHPAPI zend_class_entry *reflection_ptr; PHPAPI zend_class_entry *reflection_function_abstract_ptr; PHPAPI zend_class_entry *reflection_function_ptr; +PHPAPI zend_class_entry *reflection_generator_ptr; PHPAPI zend_class_entry *reflection_parameter_ptr; PHPAPI zend_class_entry *reflection_class_ptr; PHPAPI zend_class_entry *reflection_object_ptr; @@ -201,6 +204,7 @@ typedef struct _parameter_reference { typedef enum { REF_TYPE_OTHER, /* Must be 0 */ REF_TYPE_FUNCTION, + REF_TYPE_GENERATOR, REF_TYPE_PARAMETER, REF_TYPE_PROPERTY, REF_TYPE_DYNAMIC_PROPERTY @@ -314,6 +318,8 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ zend_string_release(prop_reference->prop.name); efree(intern->ptr); break; + case REF_TYPE_GENERATOR: + break; case REF_TYPE_OTHER: break; } @@ -2108,6 +2114,174 @@ ZEND_METHOD(reflection_function, getExtensionName) } /* }}} */ +/* {{{ proto public void ReflectionGenerator::__construct(Generator) */ +ZEND_METHOD(reflection_generator, __construct) +{ + zval *generator, *object; + reflection_object *intern; + zend_execute_data *ex; + + object = getThis(); + intern = Z_REFLECTION_P(object); + if (intern == NULL) { + return; + } + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O", &generator, zend_ce_generator) == FAILURE) { + return; + } + + ex = ((zend_generator *) Z_OBJ_P(generator))->execute_data; + if (!ex) { + zend_throw_exception(NULL, "Cannot create ReflectionGenerator based on a terminated Generator", 0); + return; + } + + intern->ref_type = REF_TYPE_GENERATOR; + ZVAL_COPY(&intern->obj, generator); + intern->ce = zend_ce_generator; +} +/* }}} */ + +#define REFLECTION_CHECK_VALID_GENERATOR(ex) \ + if (!ex) { \ + zend_throw_exception(NULL, "Cannot fetch information from a terminated Generator", 0); \ + return; \ + } + +/* {{{ proto public array ReflectionGenerator::getTrace($options = DEBUG_BACKTRACE_PROVIDE_OBJECT) */ +ZEND_METHOD(reflection_generator, getTrace) +{ + zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_generator *root_generator; + zend_execute_data *ex_backup = EG(current_execute_data); + zend_execute_data *ex = generator->execute_data; + zend_execute_data *root_prev, *cur_prev; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &options) == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + root_generator = zend_generator_get_current(generator); + + cur_prev = generator->execute_data->prev_execute_data; + if (generator == root_generator) { + generator->execute_data->prev_execute_data = NULL; + } else { + root_prev = root_generator->execute_data->prev_execute_data; + generator->execute_fake.prev_execute_data = NULL; + root_generator->execute_data->prev_execute_data = &generator->execute_fake; + } + + EG(current_execute_data) = root_generator->execute_data; + zend_fetch_debug_backtrace(return_value, 0, options, 0); + EG(current_execute_data) = ex_backup; + + root_generator->execute_data->prev_execute_data = root_prev; + generator->execute_data->prev_execute_data = cur_prev; +} +/* }}} */ + +/* {{{ proto public int ReflectionGenerator::getExecutingLine() */ +ZEND_METHOD(reflection_generator, getExecutingLine) +{ + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_execute_data *ex = generator->execute_data; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + ZVAL_LONG(return_value, ex->opline->lineno); +} +/* }}} */ + +/* {{{ proto public string ReflectionGenerator::getExecutingFile() */ +ZEND_METHOD(reflection_generator, getExecutingFile) +{ + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_execute_data *ex = generator->execute_data; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + ZVAL_STR_COPY(return_value, ex->func->op_array.filename); +} +/* }}} */ + +/* {{{ proto public ReflectionFunctionAbstract ReflectionGenerator::getFunction() */ +ZEND_METHOD(reflection_generator, getFunction) +{ + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_execute_data *ex = generator->execute_data; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + if (ex->func->common.fn_flags & ZEND_ACC_CLOSURE) { + zval closure; + ZVAL_OBJ(&closure, (zend_object *) ex->func->common.prototype); + reflection_function_factory(ex->func, &closure, return_value); + } else if (ex->func->op_array.scope) { + reflection_method_factory(ex->func->op_array.scope, ex->func, NULL, return_value); + } else { + reflection_function_factory(ex->func, NULL, return_value); + } +} +/* }}} */ + +/* {{{ proto public object ReflectionGenerator::getThis() */ +ZEND_METHOD(reflection_generator, getThis) +{ + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_execute_data *ex = generator->execute_data; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + if (Z_OBJ(ex->This)) { + ZVAL_COPY(return_value, &ex->This); + } else { + ZVAL_NULL(return_value); + } +} +/* }}} */ + +/* {{{ proto public Generator ReflectionGenerator::getExecutingGenerator() */ +ZEND_METHOD(reflection_generator, getExecutingGenerator) +{ + zend_generator *generator = (zend_generator *) Z_OBJ(Z_REFLECTION_P(getThis())->obj); + zend_execute_data *ex = generator->execute_data; + zend_generator *current; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + REFLECTION_CHECK_VALID_GENERATOR(ex) + + current = zend_generator_get_current(generator); + ++GC_REFCOUNT(current); + + ZVAL_OBJ(return_value, (zend_object *) current); +} +/* }}} */ + + /* {{{ proto public static mixed ReflectionParameter::export(mixed function, mixed parameter [, bool return]) throws ReflectionException Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ ZEND_METHOD(reflection_parameter, export) @@ -5824,6 +5998,25 @@ static const zend_function_entry reflection_function_functions[] = { PHP_FE_END }; +ZEND_BEGIN_ARG_INFO(arginfo_reflection_generator___construct, 0) + ZEND_ARG_INFO(0, generator) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_generator_trace, 0, 0, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +static const zend_function_entry reflection_generator_functions[] = { + ZEND_ME(reflection_generator, __construct, arginfo_reflection_generator___construct, 0) + ZEND_ME(reflection_generator, getExecutingLine, arginfo_reflection__void, 0) + ZEND_ME(reflection_generator, getExecutingFile, arginfo_reflection__void, 0) + ZEND_ME(reflection_generator, getTrace, arginfo_reflection_generator_trace, 0) + ZEND_ME(reflection_generator, getFunction, arginfo_reflection__void, 0) + ZEND_ME(reflection_generator, getThis, arginfo_reflection__void, 0) + ZEND_ME(reflection_generator, getExecutingGenerator, arginfo_reflection__void, 0) + PHP_FE_END +}; + ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_method_export, 0, 0, 2) ZEND_ARG_INFO(0, class) ZEND_ARG_INFO(0, name) @@ -6204,6 +6397,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ REGISTER_REFLECTION_CLASS_CONST_LONG(function, "IS_DEPRECATED", ZEND_ACC_DEPRECATED); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionGenerator", reflection_generator_functions); + _reflection_entry.create_object = reflection_objects_new; + reflection_generator_ptr = zend_register_internal_class(&_reflection_entry); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionParameter", reflection_parameter_functions); _reflection_entry.create_object = reflection_objects_new; reflection_parameter_ptr = zend_register_internal_class(&_reflection_entry); diff --git a/ext/reflection/tests/ReflectionGenerator_basic.phpt b/ext/reflection/tests/ReflectionGenerator_basic.phpt new file mode 100644 index 0000000000..528d2d9ca2 --- /dev/null +++ b/ext/reflection/tests/ReflectionGenerator_basic.phpt @@ -0,0 +1,87 @@ +--TEST-- +ReflectionGenerator basic test +--FILE-- +a(), + (function() { + yield; + })(), + foo(), +]; + +foreach ($gens as $gen) { + var_dump($gen); + + $gen->valid(); // start Generator + $ref = new ReflectionGenerator($gen); + + var_dump($ref->getTrace()); + var_dump($ref->getExecutingLine()); + var_dump($ref->getExecutingFile()); + var_dump($ref->getExecutingGenerator()); + var_dump($ref->getFunction()); + var_dump($ref->getThis()); +} + +?> +--EXPECTF-- +object(Generator)#2 (0) { +} +array(1) { + [0]=> + array(2) { + ["function"]=> + string(3) "foo" + ["args"]=> + array(0) { + } + } +} +int(%d) +string(%d) "%sReflectionGenerator_basic.%s" +object(Generator)#6 (0) { +} +object(ReflectionMethod)#8 (2) { + ["name"]=> + string(1) "a" + ["class"]=> + string(%d) "class@anonymous%s" +} +object(class@anonymous)#1 (0) { +} +object(Generator)#4 (0) { +} +array(0) { +} +int(%d) +string(%d) "%sReflectionGenerator_basic.%s" +object(Generator)#4 (0) { +} +object(ReflectionFunction)#7 (1) { + ["name"]=> + string(9) "{closure}" +} +NULL +object(Generator)#5 (0) { +} +array(0) { +} +int(%d) +string(%d) "%sReflectionGenerator_basic.%s" +object(Generator)#5 (0) { +} +object(ReflectionFunction)#8 (1) { + ["name"]=> + string(3) "foo" +} +NULL diff --git a/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt b/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt new file mode 100644 index 0000000000..00b4a56689 --- /dev/null +++ b/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt @@ -0,0 +1,65 @@ +--TEST-- +ReflectionGenerator while being currently executed +--FILE-- +$method()); +} + +function doCalls(ReflectionGenerator $ref) { + call($ref, "getTrace"); + call($ref, "getExecutingLine"); + call($ref, "getExecutingFile"); + call($ref, "getExecutingGenerator"); + call($ref, "getFunction"); + call($ref, "getThis"); +} + +($gen = (function() use (&$gen) { + $ref = new ReflectionGenerator($gen); + + doCalls($ref); + + yield from (function() use ($ref) { + doCalls($ref); + yield; // Generator ! + })(); +})())->valid(); + +?> +--EXPECTF-- +array(0) { +} +int(%d) +string(%d) "%sReflectionGenerator_in_Generator.%s" +object(Generator)#2 (0) { +} +object(ReflectionFunction)#4 (1) { + ["name"]=> + string(9) "{closure}" +} +NULL +array(1) { + [0]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } +} +int(%d) +string(%d) "%sReflectionGenerator_in_Generator.%s" +object(Generator)#5 (0) { +} +object(ReflectionFunction)#6 (1) { + ["name"]=> + string(9) "{closure}" +} +NULL -- 2.40.0