Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_func_infos.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* This is a generated file, edit the .stub.php files instead. */

static const func_info_t func_infos[] = {
F1("clone", MAY_BE_OBJECT),
F1("zend_version", MAY_BE_STRING),
FN("func_get_args", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY),
F1("get_class_vars", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF),
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/assert/expect_015.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ assert(0 && ($a = function () {
$x = $a ?? $b;
[$a, $b, $c] = [1, 2 => 'x', 'z' => 'c'];
@foo();
$y = clone $x;
$y = \clone($x);
yield 1 => 2;
yield from $x;
}))
Expand Down
31 changes: 31 additions & 0 deletions Zend/tests/clone/ast.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Ast Printing
--FILE--
<?php

$x = new stdClass();


try {
assert(false && $y = clone $x);
} catch (Error $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
assert(false && $y = clone($x));
} catch (Error $e) {
echo $e->getMessage(), PHP_EOL;
}

try {
assert(false && $y = clone(...));
} catch (Error $e) {
echo $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
assert(false && ($y = \clone($x)))
assert(false && ($y = \clone($x)))
assert(false && ($y = \clone(...)))
15 changes: 8 additions & 7 deletions Zend/tests/clone/bug36071.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ Bug #36071 (Engine Crash related with 'clone')
error_reporting=4095
--FILE--
<?php
$a = clone 0;
$a[0]->b = 0;
try {
$a = clone 0;
$a[0]->b = 0;
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECTF--
Fatal error: Uncaught Error: __clone method called on non-object in %sbug36071.php:2
Stack trace:
#0 {main}
thrown in %sbug36071.php on line 2
--EXPECT--
TypeError: clone(): Argument #1 ($object) must be of type object, int given
15 changes: 8 additions & 7 deletions Zend/tests/clone/bug42817.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
Bug #42817 (clone() on a non-object does not result in a fatal error)
--FILE--
<?php
$a = clone(null);
array_push($a->b, $c);
try {
$a = clone(null);
array_push($a->b, $c);
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECTF--
Fatal error: Uncaught Error: __clone method called on non-object in %sbug42817.php:2
Stack trace:
#0 {main}
thrown in %sbug42817.php on line 2
--EXPECT--
TypeError: clone(): Argument #1 ($object) must be of type object, null given
13 changes: 7 additions & 6 deletions Zend/tests/clone/bug42818.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
Bug #42818 ($foo = clone(array()); leaks memory)
--FILE--
<?php
$foo = clone(array());
try {
$foo = clone(array());
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECTF--
Fatal error: Uncaught Error: __clone method called on non-object in %sbug42818.php:2
Stack trace:
#0 {main}
thrown in %sbug42818.php on line 2
--EXPECT--
TypeError: clone(): Argument #1 ($object) must be of type object, array given
13 changes: 7 additions & 6 deletions Zend/tests/clone/clone_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ Using clone statement on non-object
--FILE--
<?php

$a = clone array();
try {
$a = clone array();
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

?>
--EXPECTF--
Fatal error: Uncaught Error: __clone method called on non-object in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
--EXPECT--
TypeError: clone(): Argument #1 ($object) must be of type object, array given
12 changes: 6 additions & 6 deletions Zend/tests/clone/clone_003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ Using clone statement on undefined variable
--FILE--
<?php

$a = clone $b;
try {
$a = clone $b;
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}

?>
--EXPECTF--
Warning: Undefined variable $b in %s on line %d

Fatal error: Uncaught Error: __clone method called on non-object in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
TypeError: clone(): Argument #1 ($object) must be of type object, null given
55 changes: 55 additions & 0 deletions Zend/tests/clone/clone_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--TEST--
Clone as a function.
--FILE--
<?php

$x = new stdClass();

\var_dump(\clone($x));
\var_dump(\array_map('clone', [$x, $x, $x]));
\var_dump(\array_map(clone(...), [$x, $x, $x]));

class Foo {
private function __clone() {

}

public function clone_me() {
// Verify visibility when going through array_map().
return array_map(\clone(...), [$this]);
}
}

$f = new Foo();

$clone = $f->clone_me()[0];

var_dump($f !== $clone);

?>
--EXPECTF--
object(stdClass)#%d (0) {
}
array(3) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
object(stdClass)#%d (0) {
}
[2]=>
object(stdClass)#%d (0) {
}
}
array(3) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
object(stdClass)#%d (0) {
}
[2]=>
object(stdClass)#%d (0) {
}
}
bool(true)
1 change: 1 addition & 0 deletions Zend/tests/magic_methods/bug73288.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function test_clone() {
$b = clone $c->x;
}

// No catch, because we want to test Exception::__toString().
test_clone();
?>
--EXPECTF--
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,7 @@ static void zend_disable_function(const char *function_name, size_t function_nam
if (UNEXPECTED(
(function_name_length == strlen("exit") && !memcmp(function_name, "exit", strlen("exit")))
|| (function_name_length == strlen("die") && !memcmp(function_name, "die", strlen("die")))
|| (function_name_length == strlen("clone") && !memcmp(function_name, "clone", strlen("clone")))
)) {
zend_error(E_WARNING, "Cannot disable function %s()", function_name);
return;
Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -2345,8 +2345,6 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
}
smart_str_appendc(str, '`');
break;
case ZEND_AST_CLONE:
PREFIX_OP("clone ", 270, 271);
case ZEND_AST_PRINT:
PREFIX_OP("print ", 60, 61);
case ZEND_AST_INCLUDE_OR_EVAL:
Expand Down
1 change: 0 additions & 1 deletion Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ enum _zend_ast_kind {
ZEND_AST_ISSET,
ZEND_AST_SILENCE,
ZEND_AST_SHELL_EXEC,
ZEND_AST_CLONE,
ZEND_AST_PRINT,
ZEND_AST_INCLUDE_OR_EVAL,
ZEND_AST_UNARY_OP,
Expand Down
46 changes: 46 additions & 0 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,52 @@ zend_result zend_startup_builtin_functions(void) /* {{{ */
}
/* }}} */

ZEND_FUNCTION(clone)
{
zend_object *zobj;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJ(zobj)
ZEND_PARSE_PARAMETERS_END();

zend_class_entry *scope = zend_get_executed_scope();

zend_class_entry *ce = zobj->ce;
zend_function *clone = ce->clone;

if (UNEXPECTED(zobj->handlers->clone_obj == NULL)) {
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
RETURN_THROWS();
}

if (clone && !(clone->common.fn_flags & ZEND_ACC_PUBLIC)) {
if (clone->common.scope != scope) {
if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE)
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s",
zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name),
scope ? "scope " : "global scope",
scope ? ZSTR_VAL(scope->name) : ""
);
RETURN_THROWS();
}
}
}

zend_object *cloned;
cloned = zobj->handlers->clone_obj(zobj);

if (EG(exception)) {
if (cloned) {
OBJ_RELEASE(cloned);
}

RETURN_THROWS();
}

RETURN_OBJ(cloned);
}

ZEND_FUNCTION(exit)
{
zend_string *str = NULL;
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_builtin_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ class stdClass
{
}

/** @refcount 1 */
function _clone(object $object): object {}

function exit(string|int $status = 0): never {}

/** @alias exit */
Expand Down
8 changes: 7 additions & 1 deletion Zend/zend_builtin_functions_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 16 additions & 14 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4930,6 +4930,20 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
return SUCCESS;
}

static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args)
{
znode arg_node;

if (args->children != 1) {
return FAILURE;
}

zend_compile_expr(&arg_node, args->child[0]);
zend_emit_op_tmp(result, ZEND_CLONE, &arg_node, NULL);

return SUCCESS;
}

static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
{
if (zend_string_equals_literal(lcname, "strlen")) {
Expand Down Expand Up @@ -4998,6 +5012,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
return zend_compile_func_array_key_exists(result, args);
} else if (zend_string_equals_literal(lcname, "sprintf")) {
return zend_compile_func_sprintf(result, args);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) {
return zend_compile_func_clone(result, args);
} else {
return FAILURE;
}
Expand Down Expand Up @@ -5391,17 +5407,6 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */

static void zend_compile_clone(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *obj_ast = ast->child[0];

znode obj_node;
zend_compile_expr(&obj_node, obj_ast);

zend_emit_op_tmp(result, ZEND_CLONE, &obj_node, NULL);
}
/* }}} */

static void zend_compile_global_var(zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
Expand Down Expand Up @@ -11717,9 +11722,6 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_NEW:
zend_compile_new(result, ast);
return;
case ZEND_AST_CLONE:
zend_compile_clone(result, ast);
return;
case ZEND_AST_ASSIGN_OP:
zend_compile_compound_assign(result, ast);
return;
Expand Down
Loading