Make WordPress Core

Opened 4 weeks ago

Last modified 2 weeks ago

#66100 new defect (bug)

Script Modules and Interactivity API hooks should use global instances

Reported by: jonsurrell Owned by:
Priority: normal Milestone: 7.2
Component: Script Loader Version: 6.5
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses:

Description

WP_Script_Modules::add_hooks() and WP_Interactivity_API::add_hooks() register their callbacks as array( $this, 'method' ). wp-settings.php calls both on after_setup_theme with the instance that is the global at that moment. The callbacks stay bound to that object.

Both instances live in globals so they can be replaced. r57503 made $wp_script_modules a global for that reason. Once the hooks are registered, replacing the global changes what wp_enqueue_script_module(), wp_interactivity_state(), and wp_interactivity_config() write to, but not what prints.

This is different from the behavior with classic scripts.


This was discovered while working on a fix for #64484 in ​this PR where updating the script modules global had no impact.


Related: r57503 and #56313

Change History (9)

This ticket was mentioned in ​PR #13509 on ​WordPress/wordpress-develop by ​@jonsurrell.


4 weeks ago
#1

  • Keywords has-patch has-unit-tests added

WP_Script_Modules::add_hooks() registers array( $this, 'method' ), so the print callbacks are bound to the instance that was the global on after_setup_theme. Replacing $wp_script_modules later changes what enqueues but not what prints. Scripts and styles use procedural callbacks that read the global at run time.

This adds procedural print functions to script-modules.php and registers those instead. Hook names, priorities, and the instance methods are unchanged.

Test: register the hooks, replace the global, enqueue a module, fire admin_print_footer_scripts. Fails on trunk with empty output.

Compatibility: remove_action() with the instance callback no longer matches. AMP does this in includes/class-amp-theme-support.php.

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Fable 5.1, Claude Opus
Used for: code, test, description.

---
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See ​GitHub Pull Requests for Code Review in the Core Handbook for more details.

🤖 Generated with ​Claude Code

This ticket was mentioned in ​PR #13510 on ​WordPress/wordpress-develop by ​@jonsurrell.


4 weeks ago
#2

Same problem as #13509 for WP_Interactivity_API: add_hooks() and data_wp_router_region_processor() bind callbacks to the instance. After the global is replaced, wp_interactivity_state() writes to the new instance and the filters read the old one.

This adds four procedural functions to interactivity-api.php and registers those instead. Hook names, priorities, and the instance methods are unchanged.

Test: replace the global, set state, apply script_module_data_@wordpress/interactivity. Fails on trunk with empty data. The test file's set_up() now installs its instance as the global; it registered hooks on an instance that was never the global.

Compatibility: Gutenberg lib/interactivity-api.php checks this filter by instance with has_filter() and will add its own filter too. Both set the same strings, so output is unchanged. That shim should be updated or removed.

## Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Fable 5.1, Claude Opus
Used for: code, test, description.

---
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See ​GitHub Pull Requests for Code Review in the Core Handbook for more details.

🤖 Generated with ​Claude Code

​@jonsurrell commented on ​PR #13510:


4 weeks ago
#3

Ping @darerodz for review.

​@jonsurrell commented on ​PR #13509:


4 weeks ago
#4

This was motivated by ​https://github.com/WordPress/wordpress-develop/pull/13507 originally. That work no longer depends on this change so I don't have a pressing need to land this.

However, this is a correctness issue. The change in r57503 does not have the desired impact because if the

I do share the back-compat concerns, but I'm also worried that this is a bug where the global can be modified but that doesn't actually do anything.

​@westonruter commented on ​PR #13509:


4 weeks ago
#5

The change in r57503 does not have the desired impact because if the

Because of the what?

​@jonsurrell commented on ​PR #13509:


4 weeks ago
#6

Sorry, I've corrected the updated comment. In short, the global value can be replaced but it doesn't change relevant behavior because script module hooks are registered with whatever instance was used at the after_setup_theme action that registers the hooks.

​See my updated comment for full details.

​@jonsurrell commented on ​PR #13509:


4 weeks ago
#7

There are a number of alternatives I've been considering, none of which I like. For example, the hook methods could perform the expected action if it's the global instance, or invoke the global instance otherwise. This seems like it would address back-compat _and_ the problem of not running the actions on the new global, but it also seems messy and surprising.

​@westonruter commented on ​PR #13509:


3 weeks ago
#8

In short, the global value can be replaced but it doesn't change relevant behavior because script module hooks are registered with whatever instance was used at the after_setup_theme action that registers the hooks.

Is replacing global variables like this something that is a common occurrence. It happens all the time in unit tests, but in theme/plugin code I would think that such globals are almost always left alone.

Note: See TracTickets for help on using tickets.