diff --git a/compiler/pipes/convert-invoke-to-func-call.cpp b/compiler/pipes/convert-invoke-to-func-call.cpp index 3b93064203..49d54c4f53 100644 --- a/compiler/pipes/convert-invoke-to-func-call.cpp +++ b/compiler/pipes/convert-invoke-to-func-call.cpp @@ -119,8 +119,11 @@ VertexPtr ConvertInvokeToFuncCallPass::on_clone(VertexAdaptor v_clone) return call_virt_clone; } - // clone $obj, when a class has the __clone() magic method, is replaced with { tmp = clone $obj; tmp->__clone(); $tmp } - if (klass->members.has_instance_method(ClassData::NAME_OF_CLONE)) { + // clone $obj, when a class (or any of its ancestors) has the __clone() magic method, + // is replaced with { tmp = clone $obj; tmp->__clone(); $tmp } + // find_instance_method_by_local_name walks the full parent_class chain, so an inherited + // __clone() from a base class is correctly invoked (fixes #57) + if (const auto *clone_method = klass->find_instance_method_by_local_name(ClassData::NAME_OF_CLONE)) { auto tmp_var = VertexAdaptor::create().set_location(v_clone); tmp_var->str_val = gen_unique_name("tmp_for_clone"); tmp_var->extra_type = op_ex_var_superlocal; @@ -130,7 +133,7 @@ VertexPtr ConvertInvokeToFuncCallPass::on_clone(VertexAdaptor v_clone) auto call_magic_clone = VertexAdaptor::create(tmp_var).set_location(v_clone); call_magic_clone->str_val = ClassData::NAME_OF_CLONE; call_magic_clone->extra_type = op_ex_func_call_arrow; - call_magic_clone->func_id = klass->members.get_instance_method(ClassData::NAME_OF_CLONE)->function; + call_magic_clone->func_id = clone_method->function; return VertexAdaptor::create(set_clone_to_tmp, call_magic_clone, tmp_var).set_location(v_clone); } diff --git a/tests/phpt/clone_keyword/105_inherited_clone_method.php b/tests/phpt/clone_keyword/105_inherited_clone_method.php new file mode 100644 index 0000000000..a5b4e81db5 --- /dev/null +++ b/tests/phpt/clone_keyword/105_inherited_clone_method.php @@ -0,0 +1,29 @@ +@ok +value = $v; + } + + public function __clone() { + $this->value *= 2; + } +} + +class Child extends Base { + // No __clone() here — must inherit Base::__clone() +} + +$obj = new Child(5); +$copy = clone $obj; + +// Base::__clone() doubles value: original stays 5, copy becomes 10 +var_dump($obj->value); // int(5) +var_dump($copy->value); // int(10) diff --git a/tests/phpt/clone_keyword/106_deep_inheritance_clone.php b/tests/phpt/clone_keyword/106_deep_inheritance_clone.php new file mode 100644 index 0000000000..7632d16bcb --- /dev/null +++ b/tests/phpt/clone_keyword/106_deep_inheritance_clone.php @@ -0,0 +1,27 @@ +@ok +log .= '+cloned'; + } +} + +class Parent_ extends GrandParent { + // no __clone +} + +class Child extends Parent_ { + // no __clone either — must still reach GrandParent::__clone +} + +$obj = new Child(); +$obj->log = 'original'; +$copy = clone $obj; + +var_dump($obj->log); // string(8) "original" +var_dump($copy->log); // string(15) "original+cloned"