From 2b3bebfa4cbc51f4d78cc089d31e96123aa1da20 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 10 Feb 2015 22:17:43 +0100 Subject: [PATCH] Revert removal of two ReflectionParameter functions Rather fix them for now by exempting function parameter defaults from *any* (non-ct) constant substitution (also from persistent constant substitution, which was already broken before) --- Zend/zend_compile.c | 14 +++-- Zend/zend_compile.h | 3 ++ ext/reflection/php_reflection.c | 50 ++++++++++++++++++ .../ReflectionParameter_DefaultValue.phpt | 46 ---------------- ...Parameter_DefaultValueConstant_basic1.phpt | 52 +++++++++++++++++++ ...Parameter_DefaultValueConstant_basic2.phpt | 30 +++++++++++ ...nParameter_DefaultValueConstant_error.phpt | 23 ++++++++ 7 files changed, 169 insertions(+), 49 deletions(-) delete mode 100644 ext/reflection/tests/ReflectionParameter_DefaultValue.phpt create mode 100644 ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic1.phpt create mode 100644 ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic2.phpt create mode 100644 ext/reflection/tests/ReflectionParameter_DefaultValueConstant_error.phpt diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 60d8472677..b4ce59ca35 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1179,9 +1179,9 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i /* Substitute case-sensitive (or lowercase) constants */ c = zend_hash_find_ptr(EG(zend_constants), name); if (c && ( - (c->flags & CONST_PERSISTENT) - || (Z_TYPE(c->value) < IS_OBJECT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION))) - ) { + ((c->flags & CONST_PERSISTENT) && !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)) + || (Z_TYPE(c->value) < IS_OBJECT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) + )) { ZVAL_DUP(zv, &c->value); return 1; } @@ -1224,6 +1224,10 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, return 0; } + if (CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION) { + return 0; + } + /* Substitute case-sensitive (or lowercase) persistent class constants */ if (c && Z_TYPE_P(c) < IS_OBJECT) { ZVAL_DUP(zv, c); @@ -3929,9 +3933,13 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_ "Variadic parameter cannot have a default value"); } } else if (default_ast) { + /* we cannot substitute constants here or it will break ReflectionParameter::getDefaultValueConstantName() and ReflectionParameter::isDefaultValueConstant() */ + uint32_t cops = CG(compiler_options); + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION; opcode = ZEND_RECV_INIT; default_node.op_type = IS_CONST; zend_const_expr_to_zval(&default_node.u.constant, default_ast); + CG(compiler_options) = cops; } else { opcode = ZEND_RECV; default_node.op_type = IS_UNUSED; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8a9b37cc6c..171fdcfbf2 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -927,6 +927,9 @@ END_EXTERN_C() /* disable usage of builtin instruction for strlen() */ #define ZEND_COMPILE_NO_BUILTIN_STRLEN (1<<6) +/* disable substitution of persistent constants at compile-time */ +#define ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION (1<<7) + /* The default value for CG(compiler_options) */ #define ZEND_COMPILE_DEFAULT ZEND_COMPILE_HANDLE_OP_ARRAY diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 51c2c15405..92b5d9da5f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2619,6 +2619,54 @@ ZEND_METHOD(reflection_parameter, getDefaultValue) } /* }}} */ +/* {{{ proto public bool ReflectionParameter::isDefaultValueConstant() + Returns whether the default value of this parameter is constant */ +ZEND_METHOD(reflection_parameter, isDefaultValueConstant) +{ + zend_op *precv; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + param = _reflection_param_get_default_param(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (!param) { + RETURN_FALSE; + } + + precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param); + if (precv && Z_TYPE_P(RT_CONSTANT(¶m->fptr->op_array, precv->op2)) == IS_CONSTANT) { + RETURN_TRUE; + } + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto public mixed ReflectionParameter::getDefaultValueConstantName() + Returns the default value's constant name if default value is constant or null */ +ZEND_METHOD(reflection_parameter, getDefaultValueConstantName) +{ + zend_op *precv; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + param = _reflection_param_get_default_param(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (!param) { + return; + } + + precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param); + if (precv && Z_TYPE_P(RT_CONSTANT(¶m->fptr->op_array, precv->op2)) == IS_CONSTANT) { + RETURN_STR(zend_string_copy(Z_STR_P(RT_CONSTANT(¶m->fptr->op_array, precv->op2)))); + } +} +/* }}} */ + /* {{{ proto public bool ReflectionParameter::isVariadic() Returns whether this parameter is a variadic parameter */ ZEND_METHOD(reflection_parameter, isVariadic) @@ -5990,6 +6038,8 @@ static const zend_function_entry reflection_parameter_functions[] = { ZEND_ME(reflection_parameter, isOptional, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isDefaultValueAvailable, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getDefaultValue, arginfo_reflection__void, 0) + ZEND_ME(reflection_parameter, isDefaultValueConstant, arginfo_reflection__void, 0) + ZEND_ME(reflection_parameter, getDefaultValueConstantName, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isVariadic, arginfo_reflection__void, 0) PHP_FE_END }; diff --git a/ext/reflection/tests/ReflectionParameter_DefaultValue.phpt b/ext/reflection/tests/ReflectionParameter_DefaultValue.phpt deleted file mode 100644 index b3f77ca2b5..0000000000 --- a/ext/reflection/tests/ReflectionParameter_DefaultValue.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -ReflectionParameter::getDefaultValue() with constants and non-compile-time ASTs ---FILE-- -getParameters() as $param) { - if($param->isDefaultValueAvailable()) { - var_dump($param->getDefaultValue()); - } -} - -class Foo2 { - const bar = 'Foo2::bar'; -} - -class Foo { - const bar = 'Foo::bar'; - - public function baz($param1 = self::bar, $param2 = Foo2::bar, $param3 = CONST_TEST_1 . "+string") { - } -} - -$method = new ReflectionMethod('Foo', 'baz'); -$params = $method->getParameters(); - -foreach ($params as $param) { - if ($param->isDefaultValueAvailable()) { - var_dump($param->getDefaultValue()); - } -} -?> -==DONE== ---EXPECT-- -array(0) { -} -string(6) "const1" -string(8) "Foo::bar" -string(9) "Foo2::bar" -string(13) "const1+string" -==DONE== diff --git a/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic1.phpt b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic1.phpt new file mode 100644 index 0000000000..cdd00d2624 --- /dev/null +++ b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic1.phpt @@ -0,0 +1,52 @@ +--TEST-- +ReflectionParameter::isDefaultValueConstant() && getDefaultValueConstantName() +--FILE-- +getParameters() as $param) { + if($param->getName() == 'test1') { + var_dump($param->isDefaultValueConstant()); + } + if($param->getName() == 'test2') { + var_dump($param->isDefaultValueConstant()); + } + if($param->isDefaultValueAvailable() && $param->isDefaultValueConstant()) { + var_dump($param->getDefaultValueConstantName()); + } +} + +class Foo2 { + const bar = 'Foo2::bar'; +} + +class Foo { + const bar = 'Foo::bar'; + + public function baz($param1 = self::bar, $param2=Foo2::bar, $param3=CONST_TEST_1) { + } +} + +$method = new ReflectionMethod('Foo', 'baz'); +$params = $method->getParameters(); + +foreach ($params as $param) { + if ($param->isDefaultValueConstant()) { + var_dump($param->getDefaultValueConstantName()); + } +} +?> +==DONE== +--EXPECT-- +bool(false) +bool(true) +string(12) "CONST_TEST_1" +string(9) "self::bar" +string(9) "Foo2::bar" +string(12) "CONST_TEST_1" +==DONE== diff --git a/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic2.phpt b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic2.phpt new file mode 100644 index 0000000000..1ee9e93735 --- /dev/null +++ b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_basic2.phpt @@ -0,0 +1,30 @@ +--TEST-- +ReflectionParameter::isDefaultValueConstant() && getDefaultValueConstantName() for namespace +--FILE-- +getParameters() as $param) { + if($param->isDefaultValueAvailable() && $param->isDefaultValueConstant()) { + echo $param->getDefaultValueConstantName() . "\n"; + } + } + echo "==DONE=="; +} +?> +--EXPECT-- +ReflectionTestNamespace\TestClass::TEST_CONST_2 +ReflectionTestNamespace\CONST_TEST_1 +==DONE== diff --git a/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_error.phpt b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_error.phpt new file mode 100644 index 0000000000..a2c2d24582 --- /dev/null +++ b/ext/reflection/tests/ReflectionParameter_DefaultValueConstant_error.phpt @@ -0,0 +1,23 @@ +--TEST-- +ReflectionParameter::getDefaultValueConstant() should raise exception on non optional parameter +--FILE-- +getParameters() as $param) { + try { + echo $param->getDefaultValueConstantName() . "\n"; + } + catch(ReflectionException $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +Internal error: Failed to retrieve the default value +CONST_TEST_1 -- 2.40.0