Skip to content

Commit 7def4a7

Browse files
committed
Add clone() function
1 parent 50ecaf5 commit 7def4a7

4 files changed

+88
-1
lines changed

Zend/zend_builtin_functions.c

+75
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,81 @@ zend_result zend_startup_builtin_functions(void) /* {{{ */
6969
}
7070
/* }}} */
7171

72+
ZEND_FUNCTION(clone)
73+
{
74+
zend_object *zobj;
75+
zval *args;
76+
uint32_t argc;
77+
HashTable *named_params;
78+
79+
ZEND_PARSE_PARAMETERS_START(1, -1)
80+
Z_PARAM_OBJ(zobj)
81+
Z_PARAM_VARIADIC_WITH_NAMED(args, argc, named_params);
82+
ZEND_PARSE_PARAMETERS_END();
83+
84+
zend_class_entry *scope = zend_get_executed_scope();
85+
86+
zend_class_entry *ce = zobj->ce;
87+
zend_function *clone = ce->clone;
88+
89+
if (UNEXPECTED(zobj->handlers->clone_obj == NULL)) {
90+
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
91+
RETURN_THROWS();
92+
}
93+
94+
if (clone && !(clone->common.fn_flags & ZEND_ACC_PUBLIC)) {
95+
if (clone->common.scope != scope) {
96+
if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE)
97+
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
98+
zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s",
99+
zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name),
100+
scope ? "scope " : "global scope",
101+
scope ? ZSTR_VAL(scope->name) : ""
102+
);
103+
RETURN_THROWS();
104+
}
105+
}
106+
}
107+
108+
zend_object *cloned;
109+
if (zobj->handlers->clone_obj_with) {
110+
if (UNEXPECTED(argc > 0)) {
111+
HashTable params;
112+
zend_hash_init(&params, argc + (named_params ? zend_hash_num_elements(named_params) : 0), NULL, NULL, false);
113+
for (uint32_t i = 0; i < argc; i++) {
114+
zend_hash_index_add(&params, i, &args[i]);
115+
}
116+
if (named_params != NULL) {
117+
zend_string *key;
118+
zval *val;
119+
ZEND_HASH_FOREACH_STR_KEY_VAL(named_params, key, val) {
120+
zend_hash_update(&params, key, val);
121+
} ZEND_HASH_FOREACH_END();
122+
}
123+
cloned = zobj->handlers->clone_obj_with(zobj, scope, &params);
124+
zend_hash_destroy(&params);
125+
} else {
126+
cloned = zobj->handlers->clone_obj_with(zobj, scope, named_params);
127+
}
128+
} else {
129+
if (UNEXPECTED(named_params || argc > 0)) {
130+
zend_throw_error(NULL, "Trying to clone an object with updated properties that is not compatible %s", ZSTR_VAL(ce->name));
131+
RETURN_THROWS();
132+
}
133+
cloned = zobj->handlers->clone_obj(zobj);
134+
}
135+
136+
if (EG(exception)) {
137+
if (cloned) {
138+
OBJ_RELEASE(cloned);
139+
}
140+
141+
RETURN_THROWS();
142+
}
143+
144+
RETURN_OBJ(cloned);
145+
}
146+
72147
ZEND_FUNCTION(exit)
73148
{
74149
zend_string *str = NULL;

Zend/zend_builtin_functions.stub.php

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class stdClass
77
{
88
}
99

10+
function _clone(object $object, mixed ...$updatedProperties): object {}
11+
1012
function exit(string|int $status = 0): never {}
1113

1214
/** @alias exit */

Zend/zend_builtin_functions_arginfo.h

+8-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/gen_stub.php

+3
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,9 @@ class FunctionName implements FunctionOrMethodName {
983983
private /* readonly */ Name $name;
984984

985985
public function __construct(Name $name) {
986+
if ($name->name === '_clone') {
987+
$name = new Name('clone', $name->getAttributes());
988+
}
986989
$this->name = $name;
987990
}
988991

0 commit comments

Comments
 (0)