diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0945c9574c62..65462bb8652f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3420,6 +3420,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 +3939,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_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"