@@ -633,22 +633,37 @@ PHP_METHOD(SplObjectStorage, removeAllExcept)
633633 spl_SplObjectStorage * intern = Z_SPLOBJSTORAGE_P (ZEND_THIS );
634634 spl_SplObjectStorage * other ;
635635 spl_SplObjectStorageElement * element ;
636+ zend_object * * to_remove = NULL ;
637+ uint32_t to_remove_count = 0 ;
638+ uint32_t to_remove_capacity = 0 ;
636639
637640 if (zend_parse_parameters (ZEND_NUM_ARGS (), "O" , & obj , spl_ce_SplObjectStorage ) == FAILURE ) {
638641 RETURN_THROWS ();
639642 }
640643
641644 other = Z_SPLOBJSTORAGE_P (obj );
642645
646+ /* Avoid mutating this storage while other->getHash() may re-enter it. */
643647 SPL_SAFE_HASH_FOREACH_PTR (& intern -> storage , element ) {
644648 zend_object * elem_obj = element -> obj ;
645649 GC_ADDREF (elem_obj );
646650 if (!spl_object_storage_contains (other , elem_obj )) {
647- spl_object_storage_detach (intern , elem_obj );
651+ if (to_remove_count == to_remove_capacity ) {
652+ to_remove_capacity = to_remove_capacity ? to_remove_capacity * 2 : 8 ;
653+ to_remove = safe_erealloc (to_remove , to_remove_capacity , sizeof (zend_object * ), 0 );
654+ }
655+ to_remove [to_remove_count ++ ] = elem_obj ;
656+ continue ;
648657 }
649658 OBJ_RELEASE (elem_obj );
650659 } ZEND_HASH_FOREACH_END ();
651660
661+ for (uint32_t i = 0 ; i < to_remove_count ; i ++ ) {
662+ spl_object_storage_detach (intern , to_remove [i ]);
663+ OBJ_RELEASE (to_remove [i ]);
664+ }
665+ efree (to_remove );
666+
652667 zend_hash_internal_pointer_reset_ex (& intern -> storage , & intern -> pos );
653668 intern -> index = 0 ;
654669
0 commit comments