diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0945c9574c62..2be9c4f209ca 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3400,7 +3400,10 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ if (is_constructor) { object = ZEND_THIS; } else { - object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : reflection_method_ptr); + zend_class_entry *called_ce = execute_data->This.value.ce ? execute_data->This.value.ce : reflection_method_ptr; + if (UNEXPECTED(object_init_ex(return_value, called_ce) != SUCCESS)) { + return; + } object = return_value; } intern = Z_REFLECTION_P(object); @@ -3420,6 +3423,13 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ } efree(lcname); + if (intern->ptr) { + ZEND_ASSERT(is_constructor); + _free_function(intern->ptr); + zval_ptr_dtor(reflection_prop_name(object)); + zval_ptr_dtor(reflection_prop_class(object)); + } + ZVAL_STR_COPY(reflection_prop_name(object), mptr->common.function_name); ZVAL_STR_COPY(reflection_prop_class(object), mptr->common.scope->name); intern->ptr = mptr; @@ -3932,6 +3942,11 @@ ZEND_METHOD(ReflectionClassConstant, __construct) RETURN_THROWS(); } + if (intern->ptr) { + zval_ptr_dtor(reflection_prop_name(object)); + zval_ptr_dtor(reflection_prop_class(object)); + } + intern->ptr = constant; intern->ref_type = REF_TYPE_CLASS_CONSTANT; intern->ce = constant->ce; diff --git a/ext/reflection/tests/ReflectionClassConstant_double_construct.phpt b/ext/reflection/tests/ReflectionClassConstant_double_construct.phpt new file mode 100644 index 000000000000..efd5472c4aff --- /dev/null +++ b/ext/reflection/tests/ReflectionClassConstant_double_construct.phpt @@ -0,0 +1,33 @@ +--TEST-- +ReflectionClassConstant double construct call does not leak $name and $class +--FILE-- +__construct(C::class, implode('', ['F', 'O', 'O'])); +} + +$r = new ReflectionClassConstant(C::class, 'FOO'); +for ($i = 0; $i < 10; $i++) { + test($r); +} + +$before = memory_get_usage(); +for ($i = 0; $i < 1000; $i++) { + test($r); +} +$after = memory_get_usage(); + +var_dump($before === $after); +var_dump($r->name, $r->class); + +?> +--EXPECT-- +bool(true) +string(3) "FOO" +string(1) "C" diff --git a/ext/reflection/tests/ReflectionMethod_createFromMethodName_abstract.phpt b/ext/reflection/tests/ReflectionMethod_createFromMethodName_abstract.phpt new file mode 100644 index 000000000000..c77623176fd4 --- /dev/null +++ b/ext/reflection/tests/ReflectionMethod_createFromMethodName_abstract.phpt @@ -0,0 +1,23 @@ +--TEST-- +ReflectionMethod::createFromMethodName() called on an abstract subclass +--FILE-- +getMessage(), PHP_EOL; +} + +var_dump(ReflectionMethod::createFromMethodName('C::a')->name); + +?> +--EXPECT-- +Error: Cannot instantiate abstract class R +string(1) "a" diff --git a/ext/reflection/tests/ReflectionMethod_double_construct_closure.phpt b/ext/reflection/tests/ReflectionMethod_double_construct_closure.phpt new file mode 100644 index 000000000000..3642974d9a6a --- /dev/null +++ b/ext/reflection/tests/ReflectionMethod_double_construct_closure.phpt @@ -0,0 +1,28 @@ +--TEST-- +ReflectionMethod double construct call on Closure::__invoke() does not leak +--FILE-- +__construct(function () {}, '__invoke'); +} + +$r = new ReflectionMethod(function () {}, '__invoke'); +for ($i = 0; $i < 10; $i++) { + test($r); +} + +$before = memory_get_usage(); +for ($i = 0; $i < 1000; $i++) { + test($r); +} +$after = memory_get_usage(); + +var_dump($before === $after); +var_dump($r->name, $r->class); + +?> +--EXPECT-- +bool(true) +string(8) "__invoke" +string(7) "Closure"