Skip to content

Commit be165af

Browse files
committed
Add clone_obj_with object handler
1 parent 1ec029a commit be165af

7 files changed

+63
-10
lines changed

Zend/zend_iterators.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static const zend_object_handlers iterator_object_handlers = {
3131
iter_wrapper_free,
3232
iter_wrapper_dtor,
3333
NULL, /* clone_obj */
34+
NULL, /* clone_obj_with */
3435
NULL, /* prop read */
3536
NULL, /* prop write */
3637
NULL, /* read dim */

Zend/zend_lazy_objects.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ ZEND_API HashTable *zend_lazy_object_get_properties(zend_object *object)
709709

710710
/* Initialize object and clone it. For proxies, we clone both the proxy and its
711711
* real instance, and we don't call __clone() on the proxy. */
712-
zend_object *zend_lazy_object_clone(zend_object *old_obj)
712+
zend_object *zend_lazy_object_clone(zend_object *old_obj, zend_class_entry *scope, HashTable *properties)
713713
{
714714
ZEND_ASSERT(zend_object_is_lazy(old_obj));
715715

@@ -724,7 +724,7 @@ zend_object *zend_lazy_object_clone(zend_object *old_obj)
724724
}
725725

726726
if (!zend_object_is_lazy_proxy(old_obj)) {
727-
return zend_objects_clone_obj(old_obj);
727+
return zend_objects_clone_obj_with(old_obj, scope, properties);
728728
}
729729

730730
zend_lazy_object_info *info = zend_lazy_object_get_info(old_obj);
@@ -748,7 +748,7 @@ zend_object *zend_lazy_object_clone(zend_object *old_obj)
748748

749749
zend_lazy_object_info *new_info = emalloc(sizeof(*info));
750750
*new_info = *info;
751-
new_info->u.instance = zend_objects_clone_obj(info->u.instance);
751+
new_info->u.instance = zend_objects_clone_obj_with(info->u.instance, scope, properties);
752752

753753
zend_lazy_object_set_info(new_proxy, new_info);
754754

Zend/zend_lazy_objects.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ zend_object *zend_lazy_object_get_instance(zend_object *obj);
7171
zend_lazy_object_flags_t zend_lazy_object_get_flags(zend_object *obj);
7272
void zend_lazy_object_del_info(zend_object *obj);
7373
ZEND_API HashTable *zend_lazy_object_get_properties(zend_object *object);
74-
zend_object *zend_lazy_object_clone(zend_object *old_obj);
74+
zend_object *zend_lazy_object_clone(zend_object *old_obj, zend_class_entry *scope, HashTable *properties);
7575
HashTable *zend_lazy_object_debug_info(zend_object *object, int *is_temp);
7676
HashTable *zend_lazy_object_get_gc(zend_object *zobj, zval **table, int *n);
7777
bool zend_lazy_object_decr_lazy_props(zend_object *obj);

Zend/zend_object_handlers.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,7 @@ ZEND_API const zend_object_handlers std_object_handlers = {
25222522
zend_object_std_dtor, /* free_obj */
25232523
zend_objects_destroy_object, /* dtor_obj */
25242524
zend_objects_clone_obj, /* clone_obj */
2525+
zend_objects_clone_obj_with, /* clone_obj_with */
25252526

25262527
zend_std_read_property, /* read_property */
25272528
zend_std_write_property, /* write_property */

Zend/zend_object_handlers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ typedef void (*zend_object_free_obj_t)(zend_object *object);
180180
typedef void (*zend_object_dtor_obj_t)(zend_object *object);
181181

182182
typedef zend_object* (*zend_object_clone_obj_t)(zend_object *object);
183+
typedef zend_object* (*zend_object_clone_obj_with_t)(zend_object *object, zend_class_entry *scope, HashTable *properties);
183184

184185
/* Get class name for display in var_dump and other debugging functions.
185186
* Must be defined and must return a non-NULL value. */
@@ -209,6 +210,7 @@ struct _zend_object_handlers {
209210
zend_object_free_obj_t free_obj; /* required */
210211
zend_object_dtor_obj_t dtor_obj; /* required */
211212
zend_object_clone_obj_t clone_obj; /* optional */
213+
zend_object_clone_obj_with_t clone_obj_with; /* optional */
212214
zend_object_read_property_t read_property; /* required */
213215
zend_object_write_property_t write_property; /* required */
214216
zend_object_read_dimension_t read_dimension; /* required */

Zend/zend_objects.c

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,9 @@ ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
213213
return object;
214214
}
215215

216-
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
216+
ZEND_API void ZEND_FASTCALL zend_objects_clone_members_ex(zend_object *new_object, zend_object *old_object, zend_class_entry *scope, HashTable *properties)
217217
{
218-
bool has_clone_method = old_object->ce->clone != NULL;
218+
bool has_clone_method = old_object->ce->clone != NULL || properties != NULL;
219219

220220
if (old_object->ce->default_properties_count) {
221221
zval *src = old_object->properties_table;
@@ -289,7 +289,29 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
289289

290290
if (has_clone_method) {
291291
GC_ADDREF(new_object);
292-
zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
292+
if (old_object->ce->clone) {
293+
zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
294+
}
295+
296+
if (EXPECTED(!EG(exception)) && properties != NULL) {
297+
zend_ulong num_key;
298+
zend_string *key;
299+
zval *val;
300+
ZEND_HASH_FOREACH_KEY_VAL(properties, num_key, key, val) {
301+
if (UNEXPECTED(key == NULL)) {
302+
key = zend_long_to_str(num_key);
303+
zend_update_property_ex(scope, new_object, key, val);
304+
zend_string_release_ex(key, false);
305+
} else {
306+
zend_update_property_ex(scope, new_object, key, val);
307+
}
308+
309+
if (UNEXPECTED(EG(exception))) {
310+
break;
311+
}
312+
} ZEND_HASH_FOREACH_END();
313+
}
314+
293315

294316
if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) {
295317
for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
@@ -303,12 +325,33 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
303325
}
304326
}
305327

306-
ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
328+
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
329+
{
330+
ZEND_ASSERT(old_object->ce == new_object->ce);
331+
332+
zend_objects_clone_members_ex(new_object, old_object, old_object->ce, NULL);
333+
}
334+
335+
ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, zend_class_entry *scope, HashTable *properties)
307336
{
308337
zend_object *new_object;
309338

339+
/* Compatibility with code that only overrides clone_obj. */
340+
if (UNEXPECTED(old_object->handlers->clone_obj != zend_objects_clone_obj)) {
341+
if (!old_object->handlers->clone_obj) {
342+
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(old_object->ce->name));
343+
return NULL;
344+
}
345+
if (properties && zend_hash_num_elements(properties) > 0) {
346+
zend_throw_error(NULL, "Trying to clone an object with updated properties that is not compatible %s", ZSTR_VAL(old_object->ce->name));
347+
return NULL;
348+
} else {
349+
return old_object->handlers->clone_obj(old_object);
350+
}
351+
}
352+
310353
if (UNEXPECTED(zend_object_is_lazy(old_object))) {
311-
return zend_lazy_object_clone(old_object);
354+
return zend_lazy_object_clone(old_object, scope, properties);
312355
}
313356

314357
/* assume that create isn't overwritten, so when clone depends on the
@@ -325,7 +368,12 @@ ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
325368
} while (p != end);
326369
}
327370

328-
zend_objects_clone_members(new_object, old_object);
371+
zend_objects_clone_members_ex(new_object, old_object, scope, properties);
329372

330373
return new_object;
331374
}
375+
376+
ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
377+
{
378+
return zend_objects_clone_obj_with(old_object, old_object->ce, NULL);
379+
}

Zend/zend_objects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
3030
ZEND_API void zend_object_std_dtor(zend_object *object);
3131
ZEND_API void zend_objects_destroy_object(zend_object *object);
3232
ZEND_API zend_object *zend_objects_clone_obj(zend_object *object);
33+
ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *object, zend_class_entry *scope, HashTable *properties);
3334

3435
void zend_object_dtor_dynamic_properties(zend_object *object);
3536
void zend_object_dtor_property(zend_object *object, zval *p);

0 commit comments

Comments
 (0)