mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Add ReflectionGenerator class
This commit is contained in:
parent
c6a6b9746d
commit
aa3c7aa438
3 changed files with 349 additions and 0 deletions
|
@ -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);
|
||||
|
|
87
ext/reflection/tests/ReflectionGenerator_basic.phpt
Normal file
87
ext/reflection/tests/ReflectionGenerator_basic.phpt
Normal file
|
@ -0,0 +1,87 @@
|
|||
--TEST--
|
||||
ReflectionGenerator basic test
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function foo() {
|
||||
yield;
|
||||
}
|
||||
|
||||
$gens = [
|
||||
(new class() {
|
||||
function a() {
|
||||
yield from foo();
|
||||
}
|
||||
})->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
|
65
ext/reflection/tests/ReflectionGenerator_in_Generator.phpt
Normal file
65
ext/reflection/tests/ReflectionGenerator_in_Generator.phpt
Normal file
|
@ -0,0 +1,65 @@
|
|||
--TEST--
|
||||
ReflectionGenerator while being currently executed
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function call(ReflectionGenerator $ref, $method, $rec = true) {
|
||||
if ($rec) {
|
||||
call($ref, $method, false);
|
||||
return;
|
||||
}
|
||||
var_dump($ref->$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
|
Loading…
Add table
Add a link
Reference in a new issue