Skip to content

Hooks: Fix resort_active_iterations() skipping next priority on self-removal #10949

Open
mrcasual wants to merge 1 commit intoWordPress:trunkfrom
mrcasual:fix/wp-hook-resort-active-iterations-skip
Open

Hooks: Fix resort_active_iterations() skipping next priority on self-removal #10949
mrcasual wants to merge 1 commit intoWordPress:trunkfrom
mrcasual:fix/wp-hook-resort-active-iterations-skip

Conversation

@mrcasual
Copy link

@mrcasual mrcasual commented Feb 17, 2026

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

When a callback removes itself during do_action() / apply_filters() and is the sole callback at its priority, resort_active_iterations() positions the internal array pointer one step too far. The main loop's next() call then skips the following priority entirely.

Steps to reproduce

<?php

add_action( 'test_hook', '__return_true', 10 );

add_action( 'test_hook', function () {
    remove_action( 'test_hook', __FUNCTION__, 50 );
}, 50 );

add_action( 'test_hook', function () {
    error_log( 'This never runs' );
}, 100 );

do_action( 'test_hook' ); // Priority 100 is silently skipped.

The bug requires: (1) a lower priority exists, (2) the removed callback is the only one at its priority.

@github-actions
Copy link

github-actions bot commented Feb 17, 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.

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

Props mrcasual.

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

…f-removal.

When a callback removes itself during `do_action()`/`apply_filters()` and is the
sole callback at its priority, `resort_active_iterations()` repositions the
internal array pointer one position too far. The main loop's `next()` call then
skips the following priority entirely.

The fix detects when the current priority was removed (the pointer advanced
past it) and calls `prev()` so that `next()` in the calling loop lands on the
correct priority.

Fixes #64653.
@mrcasual mrcasual force-pushed the fix/wp-hook-resort-active-iterations-skip branch from 90f0d19 to 40ea968 Compare February 17, 2026 00:33
@github-actions
Copy link

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.

*
* @ticket 64653
*/
public function test_self_removing_callback_does_not_skip_next_priority() {
Copy link
Member

Choose a reason for hiding this comment

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

I can confirm this test fails without the fix applied:

1) Tests_Hooks_DoAction::test_self_removing_callback_does_not_skip_next_priority
Priority 100 should not be skipped when priority 50 removes itself during iteration.
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
 Array &0 (
     0 => 10
     1 => 50
-    2 => 100
 )

/var/www/tests/phpunit/tests/hooks/doAction.php:331

*
* @ticket 64653
*/
public function test_self_removing_callback_at_lowest_priority() {
Copy link
Member

Choose a reason for hiding this comment

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

This test does not fail without the fix applied.

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