Skip to content

Commit 5718ebd

Browse files
committed
Add clone() function
1 parent cbd6c8d commit 5718ebd

File tree

5 files changed

+71
-1
lines changed

5 files changed

+71
-1
lines changed

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,7 @@ static void zend_disable_function(const char *function_name, size_t function_nam
36413641
if (UNEXPECTED(
36423642
(function_name_length == strlen("exit") && !memcmp(function_name, "exit", strlen("exit")))
36433643
|| (function_name_length == strlen("die") && !memcmp(function_name, "die", strlen("die")))
3644+
|| (function_name_length == strlen("clone") && !memcmp(function_name, "clone", strlen("clone")))
36443645
)) {
36453646
zend_error(E_WARNING, "Cannot disable function %s()", function_name);
36463647
return;

Zend/zend_builtin_functions.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,63 @@ zend_result zend_startup_builtin_functions(void) /* {{{ */
6969
}
7070
/* }}} */
7171

72+
ZEND_FUNCTION(clone)
73+
{
74+
zend_object *zobj;
75+
HashTable *with = NULL;
76+
77+
ZEND_PARSE_PARAMETERS_START(1, 2)
78+
Z_PARAM_OBJ(zobj)
79+
Z_PARAM_OPTIONAL
80+
Z_PARAM_ARRAY_HT(with)
81+
ZEND_PARSE_PARAMETERS_END();
82+
83+
zend_class_entry *scope = zend_get_executed_scope();
84+
85+
zend_class_entry *ce = zobj->ce;
86+
zend_function *clone = ce->clone;
87+
88+
if (UNEXPECTED(zobj->handlers->clone_obj == NULL)) {
89+
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
90+
RETURN_THROWS();
91+
}
92+
93+
if (clone && !(clone->common.fn_flags & ZEND_ACC_PUBLIC)) {
94+
if (clone->common.scope != scope) {
95+
if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE)
96+
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
97+
zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s",
98+
zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name),
99+
scope ? "scope " : "global scope",
100+
scope ? ZSTR_VAL(scope->name) : ""
101+
);
102+
RETURN_THROWS();
103+
}
104+
}
105+
}
106+
107+
zend_object *cloned;
108+
if (zobj->handlers->clone_obj_with) {
109+
cloned = zobj->handlers->clone_obj_with(zobj, scope, with);
110+
} else {
111+
if (UNEXPECTED(with != NULL && zend_hash_num_elements(with) > 0)) {
112+
zend_throw_error(NULL, "Trying to clone an object with updated properties that is not compatible %s", ZSTR_VAL(ce->name));
113+
RETURN_THROWS();
114+
}
115+
cloned = zobj->handlers->clone_obj(zobj);
116+
}
117+
118+
if (EG(exception)) {
119+
if (cloned) {
120+
OBJ_RELEASE(cloned);
121+
}
122+
123+
RETURN_THROWS();
124+
}
125+
126+
RETURN_OBJ(cloned);
127+
}
128+
72129
ZEND_FUNCTION(exit)
73130
{
74131
zend_string *str = NULL;

Zend/zend_builtin_functions.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class stdClass
77
{
88
}
99

10+
function _clone(object $object, array $withProperties = []): object {}
11+
1012
function exit(string|int $status = 0): never {}
1113

1214
/** @alias exit */

Zend/zend_builtin_functions_arginfo.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/gen_stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,9 @@ class FunctionName implements FunctionOrMethodName {
10091009
private /* readonly */ Name $name;
10101010

10111011
public function __construct(Name $name) {
1012+
if ($name->name === '_clone') {
1013+
$name = new Name('clone', $name->getAttributes());
1014+
}
10121015
$this->name = $name;
10131016
}
10141017

0 commit comments

Comments
 (0)