ReflectionFunctionAbstract::getClosureUsedVariables

Make a distinction at compile time between bind types for static
  variables

  getStaticVariables remains unchanged

  Fixes #80071
This commit is contained in:
Joe Watkins committed 2021-05-21 15:25:35 +02:00
1 parent 77ce2537f7
commit b227a72285
10 files changed
+147 -15

No files matched your search

+3
View File
@@ -112,6 +112,9 @@ PHP NEWS
. Convert resource<pspell> to object \PSpell\Dictionary. (Sara)
. Convert resource<pspell config> to object \PSpell\Config. (Sara)
- Reflection:
. Implement ReflectionFunctionAbstract::getClosureUsedVariables (krakjoe)
- Sodium:
. Added the XChaCha20 stream cipher functions. (P.I.E. Security Team)
. Added the Ristretto255 functions, which are available in libsodium 1.0.18.
+15 -7
View File
@@ -234,15 +234,23 @@ static inline bool may_have_side_effects(
}
return 0;
case ZEND_BIND_STATIC:
if (op_array->static_variables
&& (opline->extended_value & ZEND_BIND_REF) != 0) {
zval *value =
(zval*)((char*)op_array->static_variables->arData +
(opline->extended_value & ~ZEND_BIND_REF));
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
/* AST may contain undefined constants */
if (op_array->static_variables) {
/* Implicit and Explicit bind static is effectively prologue of closure so
report it has side effects like RECV, RECV_INIT; This allows us to
reflect on the closure and discover used variable at runtime */
if ((opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
return 1;
}
if ((opline->extended_value & ZEND_BIND_REF) != 0) {
zval *value =
(zval*)((char*)op_array->static_variables->arData +
(opline->extended_value & ~ZEND_BIND_REF));
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
/* AST may contain undefined constants */
return 1;
}
}
}
return 0;
case ZEND_CHECK_VAR:
+6 -1
View File
@@ -6875,6 +6875,7 @@ static void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
uint32_t i;
for (i = 0; i < list->children; ++i) {
uint32_t mode = ZEND_BIND_EXPLICIT;
zend_ast *var_ast = list->child[i];
zend_string *var_name = zend_ast_get_str(var_ast);
zval zv;
@@ -6892,7 +6893,11 @@ static void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
CG(zend_lineno) = zend_ast_get_lineno(var_ast);
zend_compile_static_var_common(var_name, &zv, var_ast->attr ? ZEND_BIND_REF : 0);
if (var_ast->attr) {
mode |= ZEND_BIND_REF;
}
zend_compile_static_var_common(var_name, &zv, mode);
}
}
/* }}} */
+1
View File
@@ -1057,6 +1057,7 @@ static zend_always_inline bool zend_check_arg_send_type(const zend_function *zf,
#define ZEND_BIND_VAL 0
#define ZEND_BIND_REF 1
#define ZEND_BIND_IMPLICIT 2
#define ZEND_BIND_EXPLICIT 4
#define ZEND_RETURNS_FUNCTION (1<<0)
#define ZEND_RETURNS_VALUE (1<<1)
+1 -1
View File
@@ -8698,7 +8698,7 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
}
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT)));
if (opline->extended_value & ZEND_BIND_REF) {
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
+1 -1
View File
@@ -47434,7 +47434,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN
}
ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT)));
value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT)));
if (opline->extended_value & ZEND_BIND_REF) {
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
+49
View File
@@ -1664,6 +1664,55 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass)
}
/* }}} */
/* {{{ Returns an associative array containing the closures lexical scope variables */
ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables)
{
reflection_object *intern;
const zend_function *closure_func;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
GET_REFLECTION_OBJECT();
array_init(return_value);
if (!Z_ISUNDEF(intern->obj)) {
closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj));
if (closure_func == NULL ||
closure_func->type != ZEND_USER_FUNCTION ||
closure_func->op_array.static_variables == NULL) {
return;
}
const zend_op_array *ops = &closure_func->op_array;
HashTable *static_variables = ZEND_MAP_PTR_GET(ops->static_variables_ptr);
if (!static_variables) {
return;
}
zend_op *opline = ops->opcodes + ops->num_args;
for (; opline->opcode == ZEND_BIND_STATIC; opline++) {
if (!(opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
continue;
}
Bucket *bucket = (Bucket*)
(((char*)static_variables->arData) +
(opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT)));
if (Z_ISUNDEF(bucket->val)) {
continue;
}
zend_hash_add_new(Z_ARRVAL_P(return_value), bucket->key, &bucket->val);
Z_TRY_ADDREF(bucket->val);
}
}
} /* }}} */
/* {{{ Returns a dynamically created closure for the function */
ZEND_METHOD(ReflectionFunction, getClosure)
{
+2
View File
@@ -50,6 +50,8 @@ abstract class ReflectionFunctionAbstract implements Reflector
/** @return ReflectionClass|null */
public function getClosureScopeClass() {}
public function getClosureUsedVariables() : array {}
/** @return string|false */
public function getDocComment() {}
+9 -5
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 0b2b52d4f891a594ccfcbcc0edeec97a9e0f80e6 */
* Stub hash: 0e99b3dabead95a356ea4ffb16d99a3e939e9869 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
@@ -27,6 +27,9 @@ ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass arginfo_class_ReflectionFunctionAbstract_inNamespace
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionFunctionAbstract_getDocComment arginfo_class_ReflectionFunctionAbstract_inNamespace
#define arginfo_class_ReflectionFunctionAbstract_getEndLine arginfo_class_ReflectionFunctionAbstract_inNamespace
@@ -424,8 +427,7 @@ ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionNamedType_isBuiltin arginfo_class_ReflectionFunctionAbstract_inNamespace
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionUnionType_getTypes, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionUnionType_getTypes arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables
#define arginfo_class_ReflectionExtension___clone arginfo_class_ReflectionFunctionAbstract___clone
@@ -489,7 +491,7 @@ ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
#define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes
#define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_newInstance, 0, 0, IS_OBJECT, 0)
ZEND_END_ARG_INFO()
@@ -508,7 +510,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getCase, 0,
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_END_ARG_INFO()
#define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionUnionType_getTypes
#define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables
#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
@@ -557,6 +559,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isGenerator);
ZEND_METHOD(ReflectionFunctionAbstract, isVariadic);
ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis);
ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass);
ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables);
ZEND_METHOD(ReflectionFunctionAbstract, getDocComment);
ZEND_METHOD(ReflectionFunctionAbstract, getEndLine);
ZEND_METHOD(ReflectionFunctionAbstract, getExtension);
@@ -795,6 +798,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = {
ZEND_ME(ReflectionFunctionAbstract, isVariadic, arginfo_class_ReflectionFunctionAbstract_isVariadic, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getClosureUsedVariables, arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, ZEND_ACC_PUBLIC)
ZEND_ME(ReflectionFunctionAbstract, getExtension, arginfo_class_ReflectionFunctionAbstract_getExtension, ZEND_ACC_PUBLIC)
@@ -0,0 +1,60 @@
--TEST--
ReflectionFunctionAbstract::getClosureUsedVariables
--FILE--
<?php
$reflector = new ReflectionFunction("strlen");
var_dump($reflector->getClosureUsedVariables());
$function = function() {
static $one = 1;
};
$reflector = new ReflectionFunction($function);
var_dump($reflector->getClosureUsedVariables());
$one = 1;
$two = 2;
$function = function() use ($one, $two) {
static $three = 3;
};
$reflector = new ReflectionFunction($function);
var_dump($reflector->getClosureUsedVariables());
$function = fn() => $three = [$one];
$reflector = new ReflectionFunction($function);
var_dump($reflector->getClosureUsedVariables());
$function = function() use (&$one) {
static $three = 3;
};
$reflector = new ReflectionFunction($function);
var_dump($reflector->getClosureUsedVariables());
?>
--EXPECT--
array(0) {
}
array(0) {
}
array(2) {
["one"]=>
int(1)
["two"]=>
int(2)
}
array(1) {
["one"]=>
int(1)
}
array(1) {
["one"]=>
&int(1)
}