@@ -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 , const HashTable * properties )
217217{
218- bool has_clone_method = old_object -> ce -> clone != NULL ;
218+ bool might_update_properties = old_object -> ce -> clone != NULL || zend_hash_num_elements ( properties ) > 0 ;
219219
220220 if (old_object -> ce -> default_properties_count ) {
221221 zval * src = old_object -> properties_table ;
@@ -226,7 +226,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
226226 i_zval_ptr_dtor (dst );
227227 ZVAL_COPY_VALUE_PROP (dst , src );
228228 zval_add_ref (dst );
229- if (has_clone_method ) {
229+ if (might_update_properties ) {
230230 /* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
231231 Z_PROP_FLAG_P (dst ) |= IS_PROP_REINITABLE ;
232232 }
@@ -241,7 +241,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
241241 src ++ ;
242242 dst ++ ;
243243 } while (src != end );
244- } else if (old_object -> properties && !has_clone_method ) {
244+ } else if (old_object -> properties && !might_update_properties ) {
245245 /* fast copy */
246246 if (EXPECTED (old_object -> handlers == & std_object_handlers )) {
247247 if (EXPECTED (!(GC_FLAGS (old_object -> properties ) & IS_ARRAY_IMMUTABLE ))) {
@@ -275,7 +275,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
275275 ZVAL_COPY_VALUE (& new_prop , prop );
276276 zval_add_ref (& new_prop );
277277 }
278- if (has_clone_method ) {
278+ if (might_update_properties ) {
279279 /* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */
280280 Z_PROP_FLAG_P (& new_prop ) |= IS_PROP_REINITABLE ;
281281 }
@@ -287,9 +287,31 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
287287 } ZEND_HASH_FOREACH_END ();
288288 }
289289
290- if (has_clone_method ) {
290+ if (might_update_properties ) {
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 )) && zend_hash_num_elements (properties ) > 0 ) {
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 , & zend_empty_array );
333+ }
334+
335+ ZEND_API zend_object * zend_objects_clone_obj_with (zend_object * old_object , zend_class_entry * scope , const 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 (zend_hash_num_elements (properties ) > 0 ) {
346+ zend_throw_error (NULL , "Cloning objects of class %s with updated properties is not supported" , 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 , & zend_empty_array );
379+ }
0 commit comments