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)
/* 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;
}
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);
"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;
/* 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
}
/* }}} */
+/* {{{ 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)
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
};
+++ /dev/null
---TEST--
-ReflectionParameter::getDefaultValue() with constants and non-compile-time ASTs
---FILE--
-<?php
-
-define("CONST_TEST_1", "const1");
-
-function ReflectionParameterTest($test1=array(), $test2 = CONST_TEST_1) {
- echo $test;
-}
-$reflect = new ReflectionFunction('ReflectionParameterTest');
-foreach($reflect->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==
--- /dev/null
+--TEST--
+ReflectionParameter::isDefaultValueConstant() && getDefaultValueConstantName()
+--FILE--
+<?php
+
+define("CONST_TEST_1", "const1");
+
+function ReflectionParameterTest($test1=array(), $test2 = CONST_TEST_1) {
+ echo $test;
+}
+$reflect = new ReflectionFunction('ReflectionParameterTest');
+foreach($reflect->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==
--- /dev/null
+--TEST--
+ReflectionParameter::isDefaultValueConstant() && getDefaultValueConstantName() for namespace
+--FILE--
+<?php
+
+namespace ReflectionTestNamespace {
+ CONST TEST_CONST_1 = "Test Const 1";
+
+ class TestClass {
+ const TEST_CONST_2 = "Test Const 2 in class";
+ }
+}
+
+namespace {
+ function ReflectionParameterTest($test=ReflectionTestNamespace\TestClass::TEST_CONST_2, $test2 = ReflectionTestNamespace\CONST_TEST_1) {
+ echo $test;
+ }
+ $reflect = new ReflectionFunction('ReflectionParameterTest');
+ foreach($reflect->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==
--- /dev/null
+--TEST--
+ReflectionParameter::getDefaultValueConstant() should raise exception on non optional parameter
+--FILE--
+<?php
+
+define("CONST_TEST_1", "const1");
+
+function ReflectionParameterTest($test, $test2 = CONST_TEST_1) {
+ echo $test;
+}
+$reflect = new ReflectionFunction('ReflectionParameterTest');
+foreach($reflect->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