From 15c342ede6750d70511ee1c271518ec7b1128bf3 Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Fri, 13 Mar 2026 16:23:27 +0530 Subject: [PATCH 1/9] feat: Update register() method to have more strict checks --- src/wp-includes/class-wp-icons-registry.php | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index 2e306bec77e53..a919207ad667a 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -111,6 +111,34 @@ protected function register( $icon_name, $icon_properties ) { return false; } + if ( preg_match( '/[A-Z]+/', $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon names must not contain uppercase characters.' ), + '7.1.0' + ); + return false; + } + + $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; + if ( ! preg_match( $name_matcher, $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon names must contain a namespace prefix. Example: my-plugin/my-custom-icon' ), + '7.1.0' + ); + return false; + } + + if ( $this->is_registered( $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon is already registered.' ), + '7.1.0' + ); + return false; + } + $allowed_keys = array_fill_keys( array( 'label', 'content', 'filePath' ), 1 ); foreach ( array_keys( $icon_properties ) as $key ) { if ( ! array_key_exists( $key, $allowed_keys ) ) { From fbcf54fe22a843eca54874ab0d780c460949481e Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Fri, 13 Mar 2026 16:41:37 +0530 Subject: [PATCH 2/9] chore: Add tests --- tests/phpunit/tests/icons/wpIconsRegistry.php | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/phpunit/tests/icons/wpIconsRegistry.php diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php new file mode 100644 index 0000000000000..f68131af5401e --- /dev/null +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -0,0 +1,116 @@ +registry = WP_Icons_Registry::get_instance(); + } + + /** + * Tear down each test method. + */ + public function tear_down() { + $instance_property = new ReflectionProperty( WP_Icons_Registry::class, 'instance' ); + $instance_property->setAccessible( true ); + $instance_property->setValue( null, null ); + + $this->registry = null; + parent::tear_down(); + } + + /** + * Invokes the WP_Icons_Registry::register method on the registry instance. + * + * @param string $icon_name Icon name including namespace. + * @param array $icon_properties Icon properties (label, content, filePath). + * @return bool True if the icon was registered successfully. + */ + private function register( $icon_name, $icon_properties ) { + $method = new ReflectionMethod( $this->registry, 'register' ); + $method->setAccessible( true ); + return $method->invoke( $this->registry, $icon_name, $icon_properties ); + } + + /** + * Should reject non-string names. + * + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_invalid_non_string_names() { + $result = $this->register( 1, array() ); + $this->assertFalse( $result ); + } + + /** + * Should reject icons without a namespace. + * + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_invalid_names_without_namespace() { + $result = $this->register( 'plus', array() ); + $this->assertFalse( $result ); + } + + /** + * Should reject icons with uppercase characters. + * + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_uppercase_characters() { + $result = $this->register( 'Core/Plus', array() ); + $this->assertFalse( $result ); + } + + /** + * Should accept valid icon names. + */ + public function test_register_icon() { + $name = 'test-plugin/my-icon'; + $settings = array( + 'label' => 'My Icon', + 'content' => '', + ); + + $result = $this->register( $name, $settings ); + $this->assertTrue( $result ); + $this->assertTrue( $this->registry->is_registered( $name ) ); + } + + /** + * Should fail to re-register the same icon. + * + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_register_icon_twice() { + $name = 'test-plugin/duplicate'; + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); + + $result = $this->register( $name, $settings ); + $this->assertTrue( $result ); + $result2 = $this->register( $name, $settings ); + $this->assertFalse( $result2 ); + } +} From a5c4eeaac8d037989c5e4748747b8451f0556988 Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Mon, 16 Mar 2026 18:19:27 +0530 Subject: [PATCH 3/9] fix: Feedbacks address related to dataProvider usage --- tests/phpunit/tests/icons/wpIconsRegistry.php | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index f68131af5401e..861f14c631ca4 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -52,33 +52,35 @@ private function register( $icon_name, $icon_properties ) { } /** - * Should reject non-string names. + * Should reject invalid icon names. * + * @dataProvider data_invalid_icon_names * @expectedIncorrectUsage WP_Icons_Registry::register - */ - public function test_invalid_non_string_names() { - $result = $this->register( 1, array() ); - $this->assertFalse( $result ); - } - - /** - * Should reject icons without a namespace. * - * @expectedIncorrectUsage WP_Icons_Registry::register + * @param mixed $icon_name Icon name to test. */ - public function test_invalid_names_without_namespace() { - $result = $this->register( 'plus', array() ); + public function test_invalid_icon_names( $icon_name ) { + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); + + $result = $this->register( $icon_name, $settings ); $this->assertFalse( $result ); } /** - * Should reject icons with uppercase characters. + * Provides invalid icon names. * - * @expectedIncorrectUsage WP_Icons_Registry::register + * @return array[] */ - public function test_uppercase_characters() { - $result = $this->register( 'Core/Plus', array() ); - $this->assertFalse( $result ); + public function data_invalid_icon_names() { + return array( + 'non-string name' => array( 1 ), + 'no namespace' => array( 'plus' ), + 'uppercase characters' => array( 'Core/Plus' ), + 'invalid characters' => array( 'test/_doing_it_wrong' ), + ); } /** From c97b0b755b621fcfe46273657d15d9a32a2103ef Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Mon, 16 Mar 2026 18:21:16 +0530 Subject: [PATCH 4/9] chore: Remove positive test, was not adding value --- tests/phpunit/tests/icons/wpIconsRegistry.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index 861f14c631ca4..d71beeae565c7 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -83,21 +83,6 @@ public function data_invalid_icon_names() { ); } - /** - * Should accept valid icon names. - */ - public function test_register_icon() { - $name = 'test-plugin/my-icon'; - $settings = array( - 'label' => 'My Icon', - 'content' => '', - ); - - $result = $this->register( $name, $settings ); - $this->assertTrue( $result ); - $this->assertTrue( $this->registry->is_registered( $name ) ); - } - /** * Should fail to re-register the same icon. * From bf30902e5dab0e8a32c1a4c8088c1c9bf6a27745 Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Tue, 17 Mar 2026 10:39:21 +0530 Subject: [PATCH 5/9] chore: Fix tests --- tests/phpunit/tests/icons/wpIconsRegistry.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index d71beeae565c7..d7e45bf767ad3 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -31,7 +31,9 @@ public function set_up() { */ public function tear_down() { $instance_property = new ReflectionProperty( WP_Icons_Registry::class, 'instance' ); - $instance_property->setAccessible( true ); + if ( PHP_VERSION_ID < 80100 ) { + $instance_property->setAccessible( true ); + } $instance_property->setValue( null, null ); $this->registry = null; @@ -47,7 +49,9 @@ public function tear_down() { */ private function register( $icon_name, $icon_properties ) { $method = new ReflectionMethod( $this->registry, 'register' ); - $method->setAccessible( true ); + if ( PHP_VERSION_ID < 80100 ) { + $method->setAccessible( true ); + } return $method->invoke( $this->registry, $icon_name, $icon_properties ); } From 5552f681be5658167f2bfc43638537b9a9942164 Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Wed, 1 Apr 2026 10:34:35 +0530 Subject: [PATCH 6/9] chore: Aligns PR with the GB PR --- src/wp-includes/class-wp-icons-registry.php | 4 +- tests/phpunit/tests/icons/wpIconsRegistry.php | 63 +++++++++++-------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index a919207ad667a..ceec97b50e665 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -111,7 +111,7 @@ protected function register( $icon_name, $icon_properties ) { return false; } - if ( preg_match( '/[A-Z]+/', $icon_name ) ) { + if ( preg_match( '/[A-Z]/', $icon_name ) ) { _doing_it_wrong( __METHOD__, __( 'Icon names must not contain uppercase characters.' ), @@ -120,7 +120,7 @@ protected function register( $icon_name, $icon_properties ) { return false; } - $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; + $name_matcher = '/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/'; if ( ! preg_match( $name_matcher, $icon_name ) ) { _doing_it_wrong( __METHOD__, diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index d7e45bf767ad3..d73ed7a35e317 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -18,22 +18,24 @@ class Tests_Icons_WpIconsRegistry extends WP_UnitTestCase { */ private $registry; - /** - * Sets up the test fixture. - */ public function set_up() { parent::set_up(); $this->registry = WP_Icons_Registry::get_instance(); } - /** - * Tear down each test method. - */ public function tear_down() { $instance_property = new ReflectionProperty( WP_Icons_Registry::class, 'instance' ); + + /* + * ReflectionProperty::setAccessible is: + * - redundant as of 8.1.0, which made all properties accessible + * - deprecated as of 8.5.0 + * - needed until 8.1.0, as property `instance` is private + */ if ( PHP_VERSION_ID < 80100 ) { $instance_property->setAccessible( true ); } + $instance_property->setValue( null, null ); $this->registry = null; @@ -41,7 +43,7 @@ public function tear_down() { } /** - * Invokes the WP_Icons_Registry::register method on the registry instance. + * Invokes Gutenberg_Icons_Registry_7_1::register despite it being private * * @param string $icon_name Icon name including namespace. * @param array $icon_properties Icon properties (label, content, filePath). @@ -49,30 +51,20 @@ public function tear_down() { */ private function register( $icon_name, $icon_properties ) { $method = new ReflectionMethod( $this->registry, 'register' ); + + /* + * ReflectionMethod::setAccessible is: + * - redundant as of 8.1.0, which made all properties accessible + * - deprecated as of 8.5.0 + * - needed until 8.1.0, as property `instance` is private + */ if ( PHP_VERSION_ID < 80100 ) { $method->setAccessible( true ); } + return $method->invoke( $this->registry, $icon_name, $icon_properties ); } - /** - * Should reject invalid icon names. - * - * @dataProvider data_invalid_icon_names - * @expectedIncorrectUsage WP_Icons_Registry::register - * - * @param mixed $icon_name Icon name to test. - */ - public function test_invalid_icon_names( $icon_name ) { - $settings = array( - 'label' => 'Icon', - 'content' => '', - ); - - $result = $this->register( $icon_name, $settings ); - $this->assertFalse( $result ); - } - /** * Provides invalid icon names. * @@ -82,7 +74,7 @@ public function data_invalid_icon_names() { return array( 'non-string name' => array( 1 ), 'no namespace' => array( 'plus' ), - 'uppercase characters' => array( 'Core/Plus' ), + 'uppercase characters' => array( 'Test/Plus' ), 'invalid characters' => array( 'test/_doing_it_wrong' ), ); } @@ -104,4 +96,23 @@ public function test_register_icon_twice() { $result2 = $this->register( $name, $settings ); $this->assertFalse( $result2 ); } + + + /** + * Should fail to register icon with invalid names. + * + * @dataProvider data_invalid_icon_names + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_register_invalid_name() { + foreach ( $this->data_invalid_icon_names() as $name ) { + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); + + $result = $this->register( $name, $settings ); + $this->assertFalse( $result ); + } + } } From 40359b0806b53cb4a67cd76f689b82d7e0b4deb2 Mon Sep 17 00:00:00 2001 From: im3dabasia Date: Wed, 1 Apr 2026 17:42:01 +0530 Subject: [PATCH 7/9] chore: Fix class name --- tests/phpunit/tests/icons/wpIconsRegistry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index d73ed7a35e317..5cfc6df48101e 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -43,7 +43,7 @@ public function tear_down() { } /** - * Invokes Gutenberg_Icons_Registry_7_1::register despite it being private + * Invokes WP_Icons_Registry::register despite it being private * * @param string $icon_name Icon name including namespace. * @param array $icon_properties Icon properties (label, content, filePath). @@ -61,7 +61,7 @@ private function register( $icon_name, $icon_properties ) { if ( PHP_VERSION_ID < 80100 ) { $method->setAccessible( true ); } - + return $method->invoke( $this->registry, $icon_name, $icon_properties ); } From 93074fb5e01f6eae96c31e2c6d0f065aa7853877 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 3 Jun 2026 18:15:15 +0900 Subject: [PATCH 8/9] chore: Add @ticket to new registry unit tests Annotate the new WP_Icons_Registry tests with @ticket 64847 so each test is traceable to its Trac ticket, matching the convention used in the sibling wpRestIconsController tests. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/phpunit/tests/icons/wpIconsRegistry.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index 5cfc6df48101e..5e02e4a3c0433 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -82,6 +82,8 @@ public function data_invalid_icon_names() { /** * Should fail to re-register the same icon. * + * @ticket 64847 + * * @expectedIncorrectUsage WP_Icons_Registry::register */ public function test_register_icon_twice() { @@ -101,6 +103,8 @@ public function test_register_icon_twice() { /** * Should fail to register icon with invalid names. * + * @ticket 64847 + * * @dataProvider data_invalid_icon_names * @expectedIncorrectUsage WP_Icons_Registry::register */ From 3c40799fc094977b95bc8a00bfc5baba5b359733 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 3 Jun 2026 18:27:48 +0900 Subject: [PATCH 9/9] chore: Fix dataProvider usage in invalid name test The test declared @dataProvider but ignored the injected value and looped over the provider itself, collapsing all cases into one test. Accept the provided name as a parameter so each dataset runs as its own test case. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/phpunit/tests/icons/wpIconsRegistry.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index 5e02e4a3c0433..043bcf2f312f8 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -107,16 +107,16 @@ public function test_register_icon_twice() { * * @dataProvider data_invalid_icon_names * @expectedIncorrectUsage WP_Icons_Registry::register + * + * @param mixed $icon_name Icon name to register. */ - public function test_register_invalid_name() { - foreach ( $this->data_invalid_icon_names() as $name ) { - $settings = array( - 'label' => 'Icon', - 'content' => '', - ); + public function test_register_invalid_name( $icon_name ) { + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); - $result = $this->register( $name, $settings ); - $this->assertFalse( $result ); - } + $result = $this->register( $icon_name, $settings ); + $this->assertFalse( $result ); } }