Skip to content

Editor: Do not apply Block Hooks when generating post excerpts. - #13524

Open
MarcinDudekDev wants to merge 3 commits into
WordPress:trunkfrom
MarcinDudekDev:trac/66110
Open

MarcinDudekDev wants to merge 3 commits into
WordPress:trunkfrom
MarcinDudekDev:trac/66110

Conversation

@MarcinDudekDev

@MarcinDudekDev MarcinDudekDev commented Sep 14, 2026

Copy link
Copy Markdown

What / why

Since r59523 (Core-61074, WP 6.8), the_content carries a Block Hooks callback at priority 8 —
apply_block_hooks_to_content, swapped for apply_block_hooks_to_content_from_post_object
in r59838 (Core-61074, Core-62716). wp_trim_excerpt() already unhooks do_blocks (r56560 / Core-58682
and wp_filter_content_tags (r55850 / Core-56588) around its inner apply_filters( 'the_content' )
call, but the priority-8 callback was never unhooked. Three consequences:

  • Hooked-block markup leaks into excerpts as plain text. The callback wraps content in a
    temporary core/post-content wrapper block so first_child/last_child hooked blocks have
    an anchor; the algorithm serializes them into the content, do_blocks is unhooked so they can
    never render, and wp_strip_all_tags() strips the `` delimiters — leaving the markup a
    hooked block carries via the hooked_block filter as literal excerpt text.
    Reproduced: excerpt goes from `'Visible text.'` to `'Visible text. LEAKED HOOKED TEXT'`.
  • Guaranteed wasted work — a parse_blocks() traversal, get_hooked_blocks(), the
    hooked_block_types/hooked_block filters, and a _wp_ignored_hooked_blocks meta read on
    every generated excerpt, for output that can never be visible.
  • Wrong post context — the callback resolves context via get_post() (current loop post),
    not the post passed to wp_trim_excerpt(), so off-loop excerpts read the wrong post's
    _wp_ignored_hooked_blocks suppression.

The change

Unhook and conditionally restore apply_block_hooks_to_content_from_post_object (priority 8)
inside wp_trim_excerpt(), mirroring the existing do_blocks/wp_filter_content_tags dance and
the identical unhook core already performs in insert_hooked_blocks_into_rest_response()
(blocks.php:1559-1573). A site that deliberately unhooked the callback is left unchanged.

Testing

  • Six new PHPUnit tests in tests/phpunit/tests/formatting/wpTrimExcerpt.php: callback unhooked
    during the inner the_content run; restored afterwards; not resurrected when previously
    unhooked; Block Hooks algorithm does not run (spied via hooked_block_types, which only fires
    inside the algorithm); hooked-block markup does not leak into the excerpt; Block Hooks still
    apply to a subsequent real the_content run.
  • Negative control: the same reproduction on the code path without the fix yields the leaked
    excerpt 'Visible text. LEAKED HOOKED TEXT', confirming the bug on trunk.
  • 5283 tests pass across the formatting / post / query / REST-posts / feed groups (sequential
    sweep); PHPCS clean.

Trac ticket: https://core.trac.wordpress.org/ticket/66110

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code (Anthropic), Devin (Cognition)
Model(s): Claude Opus 4.8 (orchestration/review), Devin (implementation)
Used for: root-cause analysis, patch + test authorship, git fact-checking, and full test sweep; reviewed and taken responsibility for by the contributor.


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

wp_trim_excerpt() builds the excerpt from already-rendered content: it runs
excerpt_remove_blocks() and temporarily unhooks do_blocks() and
wp_filter_content_tags() so the excerpt is not processed a second time. The
Block Hooks callback apply_block_hooks_to_content_from_post_object(), added to
'the_content' at priority 8 in [59523], was never unhooked there and still runs
during excerpt generation.

The callback wraps the content in a temporary core/post-content wrapper block,
which anchors first/last-child hooked blocks even when no block delimiters
remain. Those blocks can never render, because do_blocks() is unhooked, and any
markup they carry via the 'hooked_block' filter leaks into the excerpt text as
plain text. The callback also resolves its context from the current loop post
rather than the post being excerpted.

Unhook and restore it alongside the existing do_blocks()/wp_filter_content_tags()
dance, matching the same unhook already performed in
insert_hooked_blocks_into_rest_response().

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 14, 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. You can also manually ask me to refresh this list by adding the props-bot label.

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

Props myththrazz, westonruter.

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

@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.

@westonruter westonruter 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.

This looks good to me. However, I will request a review from @ockham since he introduced this function to begin with.

@westonruter
westonruter requested a review from ockham September 15, 2026 19:09
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.

2 participants