Skip to content

Icons: Override WP_Icons_Registry singleton with Gutenberg icons registry#76455

Merged
t-hamano merged 22 commits into
trunkfrom
icons/refrection-class
Apr 8, 2026
Merged

Icons: Override WP_Icons_Registry singleton with Gutenberg icons registry#76455
t-hamano merged 22 commits into
trunkfrom
icons/refrection-class

Conversation

@t-hamano
Copy link
Copy Markdown
Contributor

@t-hamano t-hamano commented Mar 12, 2026

What?

This PR replaces the WP_Icons_Registry singleton with Gutenberg_Icons_Registry_7_1 so that all code using WP_Icons_Registry::get_instance() receives Gutenberg’s icon registry instead of the Core one.

Why?

WP_Icons_Registry was released in 7.0, but in order to continue expanding this class for future releases, we created Gutenberg_Icons_Registry_7_1, which inherits from the core WP_Icons_Registry. However, this Gutenberg class is only accessed via the REST API. This means that even if we add Gutenberg_Icons_Registry_7_1::{new_method}, it cannot be called as WP_Icons_Registry::{new_method}. This is problematic because the icon registry may be accessed directly in the future, without going through the REST API.

How?

This PR uses reflection to replace WP_Icons_Registry::$instance with the Gutenberg registry instance, ensuring that the WP_Icons_Registry class always has the latest methods.

Using reflection may feel a bit hacky, but as far as I know, it's the only way to override a singleton class.

Testing Instructions

This is one way to ensure that this PR works properly.

  • Insert an Icon block, set an icon, and save the post.
  • Open the frontend.
  • Make the following changes and reload your browser.
diff --git a/lib/class-wp-icons-registry-gutenberg.php b/lib/class-wp-icons-registry-gutenberg.php
index e361ab3372d..89dfe448474 100644
--- a/lib/class-wp-icons-registry-gutenberg.php
+++ b/lib/class-wp-icons-registry-gutenberg.php
@@ -50,6 +50,13 @@ class WP_Icons_Registry_Gutenberg extends WP_Icons_Registry {
                }
        }
 
+       public function get_registered_icon( $icon_name ) {
+               return array(
+                       'name'    => $icon_name,
+                       'content' => 'Rendered via Gutenberg_Icons_Registry class.',
+               );
+       }
+
        /**
         * Modified to also search in icon labels
         */

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 12, 2026

Flaky tests detected in 60bafaa.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24122294045
📝 Reported issues:

@t-hamano t-hamano self-assigned this Mar 12, 2026
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that this class was only needed internally to reference the Gutenberg icon registry. Now that we use reflection, this class should no longer be necessary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

@t-hamano t-hamano added [Type] Code Quality Issues or PRs that relate to code quality No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core [Feature] Icons Related to Icon registration API and Icon REST API labels Mar 12, 2026
@t-hamano
Copy link
Copy Markdown
Contributor Author

@mcsf @Mamaduka, I’d like to establish an approach that allows us to continue developing the Icon API in the Gutenberg plugin while fully maintaining backward compatibility with each WordPress major version. If anyone has better ideas or suggestions, I’d love to hear them 🙏

@t-hamano t-hamano marked this pull request as ready for review March 12, 2026 16:46
@t-hamano t-hamano requested a review from spacedmonkey as a code owner March 12, 2026 16:46
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 12, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: mcsf <mcsf@git.wordpress.org>
Co-authored-by: im3dabasia <im3dabasia1@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@mcsf
Copy link
Copy Markdown
Contributor

mcsf commented Mar 12, 2026

I’d like to establish an approach that allows us to continue developing the Icon API in the Gutenberg plugin while fully maintaining backward compatibility with each WordPress major version. If anyone has better ideas or suggestions, I’d love to hear them 🙏

This "feels wrong" but also makes sense. The only other way I could think of was something I described here:

[…] it's technically possible to define a derived class such that, in the end, both [base and extended] classes share a static $instance. In short, it looked a little bit like this:

class Gutenberg_Foo extends WP_Foo {
    public static hijack_instance() {
        self::$instance = new self();
    }
}
Gutenberg_Foo::hijack_instance();

echo WP_Foo::get_instance() === Gutenberg_Foo::get_instance(); // 1

… but this is very hacky. I don't know what unintended consequences it may have.

The other hacky way is using PHP reflection.


The other unknown is independent of using reflection or this technique: timing. That is, where do we hook this? In my mind, the earlier we do it, the smaller the chance that the icon registry has already been called by consumers. Meanwhile, we'd probably also want to check the original instance for any registrations and "replay" them onto the new instance, right?

for icon in original_registry:
    new_registry.register( icon.name, icon )

@t-hamano
Copy link
Copy Markdown
Contributor Author

Thanks for the review!

The other unknown is independent of using reflection or this technique: timing. That is, where do we hook this? In my mind, the earlier we do it, the smaller the chance that the icon registry has already been called by consumers.

Unfortunately, when I use hooks such as plugins_loaded and after_setup_theme that run earlier, the following warning appears:

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the gutenberg domain was triggered too early. 

Meanwhile, we'd probably also want to check the original instance for any registrations and "replay" them onto the new instance, right?

You're right, addressed in 491c9fb

@Mamaduka
Copy link
Copy Markdown
Member

We could just keep copies of the registry and REST API controllers, and sync them as needed. We're already doing the same for the global styles and theme.json. It's not fancy, but it works 😄

@t-hamano
Copy link
Copy Markdown
Contributor Author

We could just keep copies of the registry and REST API controllers, and sync them as needed.

This seems like the simplest approach👍

Comment on lines +17 to +18
class WP_REST_Icons_Controller_Gutenberg extends WP_REST_Icons_Controller {
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, there is no need to expand the core controller, but it may be necessary in the future.

@im3dabasia
Copy link
Copy Markdown
Contributor

im3dabasia commented Mar 13, 2026

@t-hamano , I ran into this while fetching the icons using the REST API. I think once this PR is shipped, it will solve the issue. Currently, the class with private access modifiers is being accessed by Gutenberg_REST_Icons_Controller_7_1, which is located inside the wp-includes/ folder.

Edit: I applied this PR's patch in my local, did not solve the issue 😢

image

Comment thread lib/class-gutenberg-rest-view-config-controller-7-1.php
@t-hamano
Copy link
Copy Markdown
Contributor Author

The tests were failing because I accidentally deleted unrelated code. I have fixed everything and it is ready for re-review.

@t-hamano t-hamano requested a review from mcsf April 2, 2026 11:10
Copy link
Copy Markdown
Contributor

@mcsf mcsf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just note the Reflection deprecations. Thanks for working on this :)

function gutenberg_override_wp_icons_registry() {
$reflection = new ReflectionClass( WP_Icons_Registry::class );
$property = $reflection->getProperty( 'instance' );
$property->setAccessible( true );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the deprecation of setAccessible, should we do as we did here?

if ( PHP_VERSION_ID < 80100 ) {
	$instance_property->setAccessible( true );
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 60bafaa

// If the original registry was already instantiated, replay any icons outside
// the `core/` namespace onto the Gutenberg registry so they are not lost.
if ( null !== $original_registry ) {
$register_method = new ReflectionMethod( WP_Icons_Registry_Gutenberg::class, 'register' );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 60bafaa

@t-hamano t-hamano merged commit 1dc1a7f into trunk Apr 8, 2026
41 of 43 checks passed
@t-hamano t-hamano deleted the icons/refrection-class branch April 8, 2026 07:44
@github-actions github-actions Bot added this to the Gutenberg 23.0 milestone Apr 8, 2026
adamsilverstein added a commit that referenced this pull request Apr 23, 2026
…omment-type

Reconcile emoji reactions with trunk's notes architecture refactor.
Drop PR's old positioning API (reflowComments, boardOffsets,
calculatedOffset, setHeights, setBlockRef) in favor of trunk's
floating prop backed by useFloatingBoard. Preserve the reactions
data flow (reactionsMap, onToggleReaction) on top of the new
architecture. Drop require statements for icons registry files
removed in trunk (#76455).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Icons Related to Icon registration API and Icon REST API No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core [Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants