diff --git a/src/wp-includes/class-wp-icon-collections-registry.php b/src/wp-includes/class-wp-icon-collections-registry.php index e7e19d02b19e0..edd2b65ce18d3 100644 --- a/src/wp-includes/class-wp-icon-collections-registry.php +++ b/src/wp-includes/class-wp-icon-collections-registry.php @@ -154,6 +154,7 @@ public function register( $collection_slug, $collection_properties ) { * Unregisters an icon collection. * * Any icons registered under the given collection are also unregistered. + * The core-admin collection cannot be unregistered. * * @since 7.1.0 * @@ -174,6 +175,15 @@ public function unregister( $collection_slug ) { return false; } + if ( 'core-admin' === $collection_slug ) { + _doing_it_wrong( + __METHOD__, + __( 'The "core-admin" icon collection cannot be unregistered.' ), + '7.2.0' + ); + return false; + } + $icons_registry = WP_Icons_Registry::get_instance(); foreach ( $icons_registry->get_registered_icons() as $icon ) { if ( isset( $icon['collection'] ) && $icon['collection'] === $collection_slug ) { diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index 6186a0b48907a..cbf747526619b 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -228,6 +228,8 @@ public function register( $icon_name, $icon_properties ) { /** * Unregisters an icon. * + * Icons in the core-admin collection cannot be unregistered. + * * @since 7.1.0 * * @param string $icon_name Namespaced icon name in the form "collection/icon-name" @@ -248,6 +250,15 @@ public function unregister( $icon_name ) { return false; } + if ( 'core-admin' === $this->registered_icons[ $icon_name ]['collection'] ) { + _doing_it_wrong( + __METHOD__, + __( 'Icons in the "core-admin" collection cannot be unregistered.' ), + '7.2.0' + ); + return false; + } + unset( $this->registered_icons[ $icon_name ] ); return true; } diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index b647a5543b5ec..39643bdc6e052 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -479,6 +479,90 @@ public function test_unregister_unknown_icon() { $this->assertFalse( $this->registry->unregister( 'test-collection/ghost' ) ); } + /** + * @ticket 66344 + * + * @covers ::unregister + * + * @expectedIncorrectUsage WP_Icons_Registry::unregister + */ + public function test_cannot_unregister_core_admin_icon() { + $this->registry->register( + 'core-admin/test-icon', + array( + 'label' => 'Admin icon', + 'content' => '', + ) + ); + $before = wp_get_icon( 'core-admin/test-icon' ); + + $this->assertNotEmpty( $before ); + $this->assertFalse( wp_unregister_icon( 'core-admin/test-icon' ) ); + $this->assertSame( $before, wp_get_icon( 'core-admin/test-icon' ) ); + } + + /** + * @ticket 66344 + * + * @covers WP_Icon_Collections_Registry::unregister + * + * @expectedIncorrectUsage WP_Icon_Collections_Registry::unregister + */ + public function test_cannot_unregister_core_admin_collection() { + $this->registry->register( + 'core-admin/test-icon', + array( + 'label' => 'Admin icon', + 'content' => '', + ) + ); + $collections = WP_Icon_Collections_Registry::get_instance(); + $collection = $collections->get_registered( 'core-admin' ); + $before = wp_get_icon( 'core-admin/test-icon' ); + + $this->assertNotEmpty( $before ); + $this->assertFalse( wp_unregister_icon_collection( 'core-admin' ) ); + $this->assertSame( $collection, $collections->get_registered( 'core-admin' ) ); + $this->assertSame( $before, wp_get_icon( 'core-admin/test-icon' ) ); + } + + /** + * @ticket 66344 + * + * @covers ::unregister + * @covers WP_Icon_Collections_Registry::unregister + */ + public function test_can_unregister_core_icons_and_collection() { + $collections = WP_Icon_Collections_Registry::get_instance(); + $collection = $collections->get_registered( 'core' ); + unset( $collection['slug'] ); + + $this->registry->register( + 'core/test-icon', + array( + 'label' => 'Core icon', + 'content' => '', + ) + ); + $this->registry->register( + 'core/cascade-icon', + array( + 'label' => 'Core icon', + 'content' => '', + ) + ); + + try { + $this->assertTrue( wp_unregister_icon( 'core/test-icon' ) ); + $this->assertFalse( $this->registry->is_registered( 'core/test-icon' ) ); + $this->assertTrue( wp_unregister_icon_collection( 'core' ) ); + $this->assertFalse( $collections->is_registered( 'core' ) ); + $this->assertFalse( $this->registry->is_registered( 'core/cascade-icon' ) ); + } finally { + $collections->register( 'core', $collection ); + } + } + /** * @ticket 64651 * diff --git a/tests/phpunit/tests/icons/wpRestIconsController.php b/tests/phpunit/tests/icons/wpRestIconsController.php index 7a06a4a88846e..5909ff516087d 100644 --- a/tests/phpunit/tests/icons/wpRestIconsController.php +++ b/tests/phpunit/tests/icons/wpRestIconsController.php @@ -17,6 +17,13 @@ class Tests_REST_WpRestIconsController extends WP_Test_REST_Controller_Testcase protected static $contributor_id; protected static $subscriber_id; + /** + * Registry instances restored after each test. + * + * @var array + */ + private $original_registries = array(); + public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { self::$admin_id = $factory->user->create( array( 'role' => 'administrator' ) ); self::$editor_id = $factory->user->create( array( 'role' => 'editor' ) ); @@ -34,17 +41,14 @@ public static function wpTearDownAfterClass() { public function set_up() { parent::set_up(); - /* - * Other suites reset the `WP_Icons_Registry` singleton, wiping the collections and - * icons that `init` only registers once. Replay the registration so order-dependent - * tests pass. `_wp_register_default_icon_collections()` registers every default - * collection at once, so drop whatever survived rather than topping up. - */ - $collections_registry = WP_Icon_Collections_Registry::get_instance(); - foreach ( array( 'core', 'core-admin' ) as $collection_slug ) { - if ( $collections_registry->is_registered( $collection_slug ) ) { - $collections_registry->unregister( $collection_slug ); + // Use fresh registries, since the core-admin collection cannot be unregistered. + foreach ( array( WP_Icon_Collections_Registry::class, WP_Icons_Registry::class ) as $class ) { + $this->original_registries[ $class ] = $class::get_instance(); + $instance_property = new ReflectionProperty( $class, 'instance' ); + if ( PHP_VERSION_ID < 80100 ) { + $instance_property->setAccessible( true ); } + $instance_property->setValue( null, null ); } _wp_register_default_icon_collections(); _wp_register_default_icons(); @@ -56,6 +60,14 @@ public function tear_down() { wp_unregister_icon_collection( $slug ); } } + foreach ( $this->original_registries as $class => $instance ) { + $instance_property = new ReflectionProperty( $class, 'instance' ); + if ( PHP_VERSION_ID < 80100 ) { + $instance_property->setAccessible( true ); + } + $instance_property->setValue( null, $instance ); + } + $this->original_registries = array(); parent::tear_down(); }