Fixed #74862: Unable to clone instance when private __clone defined

Even though __clone was implemented as private and called only from
parent class, child extending class instance could not be cloned.
This commit is contained in:
DanielCiochiu 2017-11-07 07:30:34 +02:00 committed by Nikita Popov
parent c94d091889
commit dba5a798a2
5 changed files with 106 additions and 15 deletions

2
NEWS
View file

@ -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

43
Zend/tests/bug74862.phpt Normal file
View file

@ -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) {
}

View file

@ -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) {
}

View 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();
}

View 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();
}