Skip to content

Editor: Guard entity preload filters and share entity actions/fields logic - #14716

Closed
oandregal wants to merge 14 commits into
WordPress:trunkfrom
oandregal:fix/preload-entity-paths-guard
Closed

oandregal wants to merge 14 commits into
WordPress:trunkfrom
oandregal:fix/preload-entity-paths-guard

Conversation

@oandregal

@oandregal oandregal commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Trac tickets:

Follow-up to the Entity Actions and Entity Fields APIs.

Bug fixes

  • _wp_preload_entity_actions() and _wp_preload_entity_fields() assumed $paths was an array. If an earlier block_editor_rest_api_preload_paths callback returned null, false, '', a string or an object, the editor crashed. Empty values now become an array so the entity paths are still added. Other non-array values, or a context that isn't a WP_Block_Editor_Context, are returned unchanged. $context now defaults to null, so a call with one argument no longer throws an ArgumentCountError.
  • WP_Entity_Fields_Registry::get_all_registered_field_modules() returned numeric module ids as integers (from the array keys). They're now cast to strings, matching the actions registry, so the editor script declares the id as it was registered.

Code cleanup

The actions and fields code was duplicated. It now shares two helpers in a new entity-utils.php, loaded before both APIs:

  • _wp_add_entity_preload_paths( $paths, $context, $route ): used by both preload callbacks. The single-use helpers _wp_get_post_type_fields_preload_paths() and _wp_get_site_editor_screen_post_types() are inlined into it.
  • _wp_add_entity_modules_to_editor_script( $scripts, $get_modules ): used by both admin_footer callbacks. It takes a callable, not the modules, so the registry is still only read (and its init action only fired) on pages that load the editor script.

No behavior changes beyond the two fixes.

Testing

npm run test:php -- --group entity-actions,entity-fields

A new test covers numeric module ids. The unregistered post types test now goes through _wp_preload_entity_fields().

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Implementation, tests, and this summary; reviewed by me.


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.

oandregal and others added 7 commits October 22, 2026 13:45
`_wp_preload_entity_actions()` and `_wp_preload_entity_fields()` assumed
`$paths` was an array. If an earlier `block_editor_rest_api_preload_paths`
callback returned `null`, `false`, `''`, another string or an object, the
editor crashed. Empty values are now turned into an array so the entity
paths are still added. Other non-array values, or a context that isn't a
`WP_Block_Editor_Context`, are returned unchanged. `$context` defaults to
`null` so a call with a single argument no longer throws an
`ArgumentCountError`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_wp_get_post_type_fields_preload_paths()` was only used by
`_wp_preload_entity_fields()`. The loop now lives in the callback,
matching `_wp_preload_entity_actions()`, and the helper is removed. The
test for unregistered post types now goes through the callback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_wp_preload_entity_actions()` and `_wp_preload_entity_fields()` only
differed in the route they added paths for. The shared logic now lives in
`_wp_add_entity_preload_paths()`, which takes the route, and both
callbacks call it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`WP_Entity_Fields_Registry::get_all_registered_field_modules()` returned
the module ids from the array keys, so a numeric id came back as an
integer. They are now cast to strings, matching
`WP_Entity_Actions_Registry::get_all_registered_action_modules()`, so the
editor script declares the id as it was registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_wp_add_entity_preload_paths()` is used by both the Entity Actions and
Entity Fields APIs, but lived in entity-fields.php, which is loaded after
entity-actions.php. It now lives in entity-utils.php, loaded before both,
so neither API depends on the other's file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_wp_add_entity_action_modules_to_editor_script()` and
`_wp_add_entity_field_modules_to_editor_script()` only differed in the
registry they read the script modules from. The shared logic now lives in
`_wp_add_entity_modules_to_editor_script()` in entity-utils.php, which
takes the reader of the modules, and both callbacks call it.

The reader is a callable rather than the modules themselves so the
registry is still only read, and its init action only fired, on the pages
that load the editor script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 22, 2026 12:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

Core Committers: Use this line as a base for the props when committing in SVN:

Props oandregal, jorgefilipecosta.

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


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Comment thread src/wp-includes/class-wp-entity-fields-registry.php
Comment thread src/wp-includes/class-wp-entity-fields-registry.php
Comment thread tests/phpunit/tests/entity-fields/wpPreloadEntityFields.php
Comment thread src/wp-includes/entity-utils.php
Comment thread src/wp-includes/entity-actions.php
Comment thread src/wp-includes/entity-utils.php
Comment thread src/wp-includes/entity-actions.php

@jorgefilipecosta jorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Left some comments for consideration but nothing is a blocker.

oandregal and others added 7 commits October 22, 2026 17:01
…es().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PHP turns a numeric module id into an integer array key. The entity fields
controller now casts it back, like the entity actions controller, so the
response matches its schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wp_get_registered_entity_field_modules() and
wp_get_registered_entity_action_modules() keyed their result by module id,
which PHP turns into an integer when numeric: a strict comparison misses it
and array_merge() renumbers it. They now return a list of entries with the
module id and the ids of the fields or actions it applies to, the shape the
REST controllers already return, which now use them as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
empty() treats the string '0' as empty, so a field with that id was skipped
with a notice asking for a non-empty string. Check for the empty string
instead, as the entity actions registry does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wporg-sync pushed a commit that referenced this pull request Oct 23, 2026
`_wp_preload_entity_actions()` and `_wp_preload_entity_fields()` assumed
`$paths` was an array, so the editor crashed when an earlier
`block_editor_rest_api_preload_paths` callback returned `null`, `false`,
a string or an object. Empty values are now turned into an array so the
entity paths are still added. Other non-array values, and contexts that
are not a `WP_Block_Editor_Context`, are returned unchanged. `$context`
now defaults to `null`, so calling either function with one argument no
longer throws an `ArgumentCountError`.

Numeric script module ids are now handled as strings. PHP turned them into
integer array keys, so `wp_get_registered_entity_field_modules()` and
`wp_get_registered_entity_action_modules()` could miss them in a strict
comparison and `array_merge()` could renumber them. Both functions now
return a list of entries holding the module id and the ids of the fields
or actions it applies to. This is the shape the REST controllers already
returned, and they now use these results directly. The entity fields
endpoint now returns module ids as strings, matching its schema.

A field registered with the id `'0'` is no longer rejected as empty.

The code duplicated between the two APIs now lives in a new
`entity-utils.php` file:
- `_wp_add_entity_preload_paths()` builds the preload paths for both
  preload callbacks.
- `_wp_add_entity_modules_to_editor_script()` adds the script modules to
  the editor script for both `admin_footer` callbacks. It takes a callable
  rather than the modules, so the registries are still only read on pages
  that load the editor script.

Developed in #14716.

Props oandregal, jorgefilipecosta.
See #66229, #66343.


git-svn-id: https://develop.svn.wordpress.org/trunk@64686 602fd350-edb4-49c9-b593-d223f7449a82
wporg-sync pushed a commit to WordPress/WordPress that referenced this pull request Oct 23, 2026
`_wp_preload_entity_actions()` and `_wp_preload_entity_fields()` assumed
`$paths` was an array, so the editor crashed when an earlier
`block_editor_rest_api_preload_paths` callback returned `null`, `false`,
a string or an object. Empty values are now turned into an array so the
entity paths are still added. Other non-array values, and contexts that
are not a `WP_Block_Editor_Context`, are returned unchanged. `$context`
now defaults to `null`, so calling either function with one argument no
longer throws an `ArgumentCountError`.

Numeric script module ids are now handled as strings. PHP turned them into
integer array keys, so `wp_get_registered_entity_field_modules()` and
`wp_get_registered_entity_action_modules()` could miss them in a strict
comparison and `array_merge()` could renumber them. Both functions now
return a list of entries holding the module id and the ids of the fields
or actions it applies to. This is the shape the REST controllers already
returned, and they now use these results directly. The entity fields
endpoint now returns module ids as strings, matching its schema.

A field registered with the id `'0'` is no longer rejected as empty.

The code duplicated between the two APIs now lives in a new
`entity-utils.php` file:
- `_wp_add_entity_preload_paths()` builds the preload paths for both
  preload callbacks.
- `_wp_add_entity_modules_to_editor_script()` adds the script modules to
  the editor script for both `admin_footer` callbacks. It takes a callable
  rather than the modules, so the registries are still only read on pages
  that load the editor script.

Developed in WordPress/wordpress-develop#14716.

Props oandregal, jorgefilipecosta.
See #66229, #66343.

Built from https://develop.svn.wordpress.org/trunk@64686


git-svn-id: http://core.svn.wordpress.org/trunk@63836 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@oandregal

Copy link
Copy Markdown
Member Author

@oandregal oandregal closed this Oct 23, 2026
@oandregal
oandregal deleted the fix/preload-entity-paths-guard branch October 23, 2026 07:38
@jasmussen

Copy link
Copy Markdown

Noting that I'm seeing some errors that I think are caused by this PR, with context here.

@oandregal

Copy link
Copy Markdown
Member Author

Thanks for the heads-up, I've just merged WordPress/gutenberg#85390 that should fix them. I didn't expect Gutenberg's CI to pick the last WordPress version so fast! Should I know, I'd have synced the merging of both PRs. Sorry for the inconvenience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants