@@ -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_with (zend_object * new_object , zend_object * old_object , const zend_class_entry * scope , const HashTable * properties )
192+ ZEND_API void ZEND_FASTCALL zend_objects_clone_members (zend_object * new_object , zend_object * old_object )
193193{
194- bool might_update_properties = old_object -> ce -> clone != NULL || zend_hash_num_elements ( properties ) > 0 ;
194+ bool has_clone_method = old_object -> ce -> clone != NULL ;
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_with(zend_object *new_obj
202202 i_zval_ptr_dtor (dst );
203203 ZVAL_COPY_VALUE_PROP (dst , src );
204204 zval_add_ref (dst );
205- if (might_update_properties ) {
205+ if (has_clone_method ) {
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_with(zend_object *new_obj
217217 src ++ ;
218218 dst ++ ;
219219 } while (src != end );
220- } else if (old_object -> properties && !might_update_properties ) {
220+ } else if (old_object -> properties && !has_clone_method ) {
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_with(zend_object *new_obj
251251 ZVAL_COPY_VALUE (& new_prop , prop );
252252 zval_add_ref (& new_prop );
253253 }
254- if (might_update_properties ) {
254+ if (has_clone_method ) {
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,45 +263,9 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members_with(zend_object *new_obj
263263 } ZEND_HASH_FOREACH_END ();
264264 }
265265
266- if (might_update_properties ) {
266+ if (has_clone_method ) {
267267 GC_ADDREF (new_object );
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- /* Unlock readonly properties once more. */
274- if (ZEND_CLASS_HAS_READONLY_PROPS (new_object -> ce ) && old_object -> ce -> clone ) {
275- for (uint32_t i = 0 ; i < new_object -> ce -> default_properties_count ; i ++ ) {
276- zval * prop = OBJ_PROP_NUM (new_object , i );
277- Z_PROP_FLAG_P (prop ) |= IS_PROP_REINITABLE ;
278- }
279- }
280-
281- const zend_class_entry * old_scope = EG (fake_scope );
282-
283- EG (fake_scope ) = scope ;
284-
285- zend_ulong num_key ;
286- zend_string * key ;
287- zval * val ;
288- ZEND_HASH_FOREACH_KEY_VAL (properties , num_key , key , val ) {
289- if (UNEXPECTED (key == NULL )) {
290- key = zend_long_to_str (num_key );
291- new_object -> handlers -> write_property (new_object , key , val , NULL );
292- zend_string_release_ex (key , false);
293- } else {
294- new_object -> handlers -> write_property (new_object , key , val , NULL );
295- }
296-
297- if (UNEXPECTED (EG (exception ))) {
298- break ;
299- }
300- } ZEND_HASH_FOREACH_END ();
301-
302- EG (fake_scope ) = old_scope ;
303- }
304-
268+ zend_call_known_instance_method_with_0_params (new_object -> ce -> clone , new_object , NULL );
305269
306270 if (ZEND_CLASS_HAS_READONLY_PROPS (new_object -> ce )) {
307271 for (uint32_t i = 0 ; i < new_object -> ce -> default_properties_count ; i ++ ) {
@@ -315,34 +279,58 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members_with(zend_object *new_obj
315279 }
316280}
317281
318- ZEND_API void ZEND_FASTCALL zend_objects_clone_members (zend_object * new_object , zend_object * old_object )
319- {
320- ZEND_ASSERT (old_object -> ce == new_object -> ce );
321-
322- zend_objects_clone_members_with (new_object , old_object , NULL , & zend_empty_array );
323- }
324-
325282ZEND_API zend_object * zend_objects_clone_obj_with (zend_object * old_object , const zend_class_entry * scope , const HashTable * properties )
326283{
327284 zend_object * new_object ;
328285
329- /* Compatibility with code that only overrides clone_obj. */
330- if (UNEXPECTED (old_object -> handlers -> clone_obj != zend_objects_clone_obj )) {
331- if (!old_object -> handlers -> clone_obj ) {
332- zend_throw_error (NULL , "Trying to clone an uncloneable object of class %s" , ZSTR_VAL (old_object -> ce -> name ));
333- return NULL ;
334- }
286+ new_object = old_object -> handlers -> clone_obj (old_object );
287+
288+ if (EXPECTED (!EG (exception )) && zend_hash_num_elements (properties ) > 0 ) {
289+ GC_ADDREF (new_object );
335290
336- if (zend_hash_num_elements (properties ) > 0 ) {
337- zend_throw_error (NULL , "Cloning objects of class %s with updated properties is not supported" , ZSTR_VAL (old_object -> ce -> name ));
338- return NULL ;
291+ /* Unlock readonly properties once more. */
292+ if (ZEND_CLASS_HAS_READONLY_PROPS (new_object -> ce )) {
293+ for (uint32_t i = 0 ; i < new_object -> ce -> default_properties_count ; i ++ ) {
294+ zval * prop = OBJ_PROP_NUM (new_object , i );
295+ Z_PROP_FLAG_P (prop ) |= IS_PROP_REINITABLE ;
296+ }
339297 }
340298
341- return old_object -> handlers -> clone_obj (old_object );
299+ const zend_class_entry * old_scope = EG (fake_scope );
300+
301+ EG (fake_scope ) = scope ;
302+
303+ zend_ulong num_key ;
304+ zend_string * key ;
305+ zval * val ;
306+ ZEND_HASH_FOREACH_KEY_VAL (properties , num_key , key , val ) {
307+ if (UNEXPECTED (key == NULL )) {
308+ key = zend_long_to_str (num_key );
309+ new_object -> handlers -> write_property (new_object , key , val , NULL );
310+ zend_string_release_ex (key , false);
311+ } else {
312+ new_object -> handlers -> write_property (new_object , key , val , NULL );
313+ }
314+
315+ if (UNEXPECTED (EG (exception ))) {
316+ break ;
317+ }
318+ } ZEND_HASH_FOREACH_END ();
319+
320+ EG (fake_scope ) = old_scope ;
321+
322+ OBJ_RELEASE (new_object );
342323 }
343324
325+ return new_object ;
326+ }
327+
328+ ZEND_API zend_object * zend_objects_clone_obj (zend_object * old_object )
329+ {
330+ zend_object * new_object ;
331+
344332 if (UNEXPECTED (zend_object_is_lazy (old_object ))) {
345- return zend_lazy_object_clone (old_object , scope , properties );
333+ return zend_lazy_object_clone (old_object );
346334 }
347335
348336 /* assume that create isn't overwritten, so when clone depends on the
@@ -359,12 +347,7 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const
359347 } while (p != end );
360348 }
361349
362- zend_objects_clone_members_with (new_object , old_object , scope , properties );
350+ zend_objects_clone_members (new_object , old_object );
363351
364352 return new_object ;
365353}
366-
367- ZEND_API zend_object * zend_objects_clone_obj (zend_object * old_object )
368- {
369- return zend_objects_clone_obj_with (old_object , NULL , & zend_empty_array );
370- }
0 commit comments