Skip to content

Commit 934fcad

Browse files
committed
zend_objects: Add clone_obj_with object handler
1 parent b43a7ac commit 934fcad

File tree

7 files changed

+69
-14
lines changed

7 files changed

+69
-14
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, const zend_class_entry *scope, const 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(const zend_object *obj);
7272
void zend_lazy_object_del_info(const 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, const zend_class_entry *scope, const 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(const zend_object *obj);

Zend/zend_object_handlers.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,7 @@ ZEND_API const zend_object_handlers std_object_handlers = {
25412541
zend_object_std_dtor, /* free_obj */
25422542
zend_objects_destroy_object, /* dtor_obj */
25432543
zend_objects_clone_obj, /* clone_obj */
2544+
zend_objects_clone_obj_with, /* clone_obj_with */
25442545

25452546
zend_std_read_property, /* read_property */
25462547
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, const zend_class_entry *scope, const 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: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
189189
return object;
190190
}
191191

192-
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
192+
ZEND_API void ZEND_FASTCALL zend_objects_clone_members_with(zend_object *new_object, zend_object *old_object, const zend_class_entry *scope, const HashTable *properties)
193193
{
194-
bool has_clone_method = old_object->ce->clone != NULL;
194+
bool might_update_properties = old_object->ce->clone != NULL || zend_hash_num_elements(properties) > 0;
195195

196196
if (old_object->ce->default_properties_count) {
197197
zval *src = old_object->properties_table;
@@ -202,7 +202,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
202202
i_zval_ptr_dtor(dst);
203203
ZVAL_COPY_VALUE_PROP(dst, src);
204204
zval_add_ref(dst);
205-
if (has_clone_method) {
205+
if (might_update_properties) {
206206
/* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
207207
Z_PROP_FLAG_P(dst) |= IS_PROP_REINITABLE;
208208
}
@@ -217,7 +217,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
217217
src++;
218218
dst++;
219219
} while (src != end);
220-
} else if (old_object->properties && !has_clone_method) {
220+
} else if (old_object->properties && !might_update_properties) {
221221
/* fast copy */
222222
if (EXPECTED(old_object->handlers == &std_object_handlers)) {
223223
if (EXPECTED(!(GC_FLAGS(old_object->properties) & IS_ARRAY_IMMUTABLE))) {
@@ -251,7 +251,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
251251
ZVAL_COPY_VALUE(&new_prop, prop);
252252
zval_add_ref(&new_prop);
253253
}
254-
if (has_clone_method) {
254+
if (might_update_properties) {
255255
/* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
256256
Z_PROP_FLAG_P(&new_prop) |= IS_PROP_REINITABLE;
257257
}
@@ -263,9 +263,31 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
263263
} ZEND_HASH_FOREACH_END();
264264
}
265265

266-
if (has_clone_method) {
266+
if (might_update_properties) {
267267
GC_ADDREF(new_object);
268-
zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
268+
if (old_object->ce->clone) {
269+
zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
270+
}
271+
272+
if (EXPECTED(!EG(exception)) && zend_hash_num_elements(properties) > 0) {
273+
zend_ulong num_key;
274+
zend_string *key;
275+
zval *val;
276+
ZEND_HASH_FOREACH_KEY_VAL(properties, num_key, key, val) {
277+
if (UNEXPECTED(key == NULL)) {
278+
key = zend_long_to_str(num_key);
279+
zend_update_property_ex(scope, new_object, key, val);
280+
zend_string_release_ex(key, false);
281+
} else {
282+
zend_update_property_ex(scope, new_object, key, val);
283+
}
284+
285+
if (UNEXPECTED(EG(exception))) {
286+
break;
287+
}
288+
} ZEND_HASH_FOREACH_END();
289+
}
290+
269291

270292
if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) {
271293
for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
@@ -279,12 +301,34 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
279301
}
280302
}
281303

282-
ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
304+
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
305+
{
306+
ZEND_ASSERT(old_object->ce == new_object->ce);
307+
308+
zend_objects_clone_members_with(new_object, old_object, old_object->ce, &zend_empty_array);
309+
}
310+
311+
ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const zend_class_entry *scope, const HashTable *properties)
283312
{
284313
zend_object *new_object;
285314

315+
/* Compatibility with code that only overrides clone_obj. */
316+
if (UNEXPECTED(old_object->handlers->clone_obj != zend_objects_clone_obj)) {
317+
if (!old_object->handlers->clone_obj) {
318+
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(old_object->ce->name));
319+
return NULL;
320+
}
321+
322+
if (zend_hash_num_elements(properties) > 0) {
323+
zend_throw_error(NULL, "Cloning objects of class %s with updated properties is not supported", ZSTR_VAL(old_object->ce->name));
324+
return NULL;
325+
}
326+
327+
return old_object->handlers->clone_obj(old_object);
328+
}
329+
286330
if (UNEXPECTED(zend_object_is_lazy(old_object))) {
287-
return zend_lazy_object_clone(old_object);
331+
return zend_lazy_object_clone(old_object, scope, properties);
288332
}
289333

290334
/* assume that create isn't overwritten, so when clone depends on the
@@ -301,7 +345,12 @@ ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
301345
} while (p != end);
302346
}
303347

304-
zend_objects_clone_members(new_object, old_object);
348+
zend_objects_clone_members_with(new_object, old_object, scope, properties);
305349

306350
return new_object;
307351
}
352+
353+
ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
354+
{
355+
return zend_objects_clone_obj_with(old_object, old_object->ce, &zend_empty_array);
356+
}

Zend/zend_objects.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ BEGIN_EXTERN_C()
2626
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce);
2727
ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce);
2828
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object);
29+
ZEND_API void ZEND_FASTCALL zend_objects_clone_members_with(zend_object *new_object, zend_object *old_object, const zend_class_entry *scope, const HashTable *properties);
2930

3031
ZEND_API void zend_object_std_dtor(zend_object *object);
3132
ZEND_API void zend_objects_destroy_object(zend_object *object);
3233
ZEND_API zend_object *zend_objects_clone_obj(zend_object *object);
34+
ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *object, const zend_class_entry *scope, const HashTable *properties);
3335

3436
void zend_object_dtor_dynamic_properties(zend_object *object);
3537
void zend_object_dtor_property(zend_object *object, zval *p);

0 commit comments

Comments
 (0)