]> granicus.if.org Git - php/commitdiff
Fixed #74862: Unable to clone instance when private __clone defined
authorDanielCiochiu <daniel@ciochiu.ro>
Tue, 7 Nov 2017 05:30:34 +0000 (07:30 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 15 Nov 2017 22:00:26 +0000 (23:00 +0100)
Even though __clone was implemented as private and called only from
parent class, child extending class instance could not be cloned.

NEWS
Zend/tests/bug74862.phpt [new file with mode: 0644]
Zend/tests/bug74862_2.phpt [new file with mode: 0644]
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index 2161afaa525638c4082c5f0868c33a56b68c59cf..d5b94bd8094f445bb350e7bf3186f57eccfffcb4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ PHP                                                                        NEWS
 - Core:
   . Fixed bug #75384 (PHP seems incompatible with OneDrive files on demand).
     (Anatol)
+  . Fixed bug #74862 (Unable to clone instance when private __clone defined).
+    (Daniel Ciochiu)
 
 - CLI Server:
   . Fixed bug #60471 (Random "Invalid request (unexpected EOF)" using a router
diff --git a/Zend/tests/bug74862.phpt b/Zend/tests/bug74862.phpt
new file mode 100644 (file)
index 0000000..7822575
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+Bug #74862 (Unable to clone instance when private __clone defined)
+--FILE--
+<?php
+
+class a {
+    private function __clone()
+    {
+
+    }
+
+    private function __construct()
+    {
+
+    }
+
+    public static function getInstance()
+    {
+        return new static();
+    }
+
+    public function cloneIt()
+    {
+        $a = clone $this;
+
+        return $a;
+    }
+}
+
+class c extends a {
+
+}
+
+// private constructor
+$d = c::getInstance();
+
+// private clone
+$e = $d->cloneIt();
+var_dump($e);
+?>
+--EXPECT--
+object(c)#2 (0) {
+}
diff --git a/Zend/tests/bug74862_2.phpt b/Zend/tests/bug74862_2.phpt
new file mode 100644 (file)
index 0000000..2e544a3
--- /dev/null
@@ -0,0 +1,46 @@
+--TEST--
+Bug #74862 (Unable to clone instance when private __clone defined in a child class)
+--FILE--
+<?php
+
+class main {
+}
+
+class a extends main {
+    private function __clone()
+    {
+
+    }
+
+    private function __construct()
+    {
+
+    }
+
+    public static function getInstance()
+    {
+        return new static();
+    }
+
+    public function cloneIt()
+    {
+        $a = clone $this;
+
+        return $a;
+    }
+}
+
+class c extends a {
+
+}
+
+// private constructor
+$d = c::getInstance();
+
+// private clone
+$e = $d->cloneIt();
+var_dump($e);
+?>
+--EXPECT--
+object(c)#2 (0) {
+}
index 9a633780b1aa11d8986efc9fb456d52b3b30ebff..08eb762a9996136738202c532f460ff206955d04 100644 (file)
@@ -5080,8 +5080,8 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
                        /* Ensure that if we're calling a private function, we're allowed to do so.
                         */
                        scope = EX(func)->op_array.scope;
-                       if (UNEXPECTED(ce != scope)) {
-                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                       if (!zend_check_private(clone, scope, clone->common.function_name)) {
+                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
                                FREE_OP1();
                                HANDLE_EXCEPTION();
                        }
@@ -5090,7 +5090,7 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
                         */
                        scope = EX(func)->op_array.scope;
                        if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
-                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
                                FREE_OP1();
                                HANDLE_EXCEPTION();
                        }
index bb4f50a431d1fc28db79feb85521b3ea3ea891a7..94949e8881e06b2cb2b218c3acb429b029acdc2c 100644 (file)
@@ -3307,8 +3307,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_
                        /* Ensure that if we're calling a private function, we're allowed to do so.
                         */
                        scope = EX(func)->op_array.scope;
-                       if (UNEXPECTED(ce != scope)) {
-                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                       if (!zend_check_private(clone, scope, clone->common.function_name)) {
+                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -3317,7 +3317,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_
                         */
                        scope = EX(func)->op_array.scope;
                        if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
-                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -28031,8 +28031,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND
                        /* Ensure that if we're calling a private function, we're allowed to do so.
                         */
                        scope = EX(func)->op_array.scope;
-                       if (UNEXPECTED(ce != scope)) {
-                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                       if (!zend_check_private(clone, scope, clone->common.function_name)) {
+                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -28041,7 +28041,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND
                         */
                        scope = EX(func)->op_array.scope;
                        if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
-                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -35361,8 +35361,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC
                        /* Ensure that if we're calling a private function, we're allowed to do so.
                         */
                        scope = EX(func)->op_array.scope;
-                       if (UNEXPECTED(ce != scope)) {
-                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                       if (!zend_check_private(clone, scope, clone->common.function_name)) {
+                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -35371,7 +35371,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC
                         */
                        scope = EX(func)->op_array.scope;
                        if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
-                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
 
                                HANDLE_EXCEPTION();
                        }
@@ -51620,8 +51620,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND
                        /* Ensure that if we're calling a private function, we're allowed to do so.
                         */
                        scope = EX(func)->op_array.scope;
-                       if (UNEXPECTED(ce != scope)) {
-                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                       if (!zend_check_private(clone, scope, clone->common.function_name)) {
+                               zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
                                zval_ptr_dtor_nogc(free_op1);
                                HANDLE_EXCEPTION();
                        }
@@ -51630,7 +51630,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND
                         */
                        scope = EX(func)->op_array.scope;
                        if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
-                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
+                               zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : "");
                                zval_ptr_dtor_nogc(free_op1);
                                HANDLE_EXCEPTION();
                        }