Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
WP Trac 17817 Release Notes

WordPress 4.2 introduces a significant reworking of action and filter iteration to address bugs that arose from recursive callbacks and from callbacks that changed the hooked callbacks on currently running actions/filters.

What does this mean for you, the plugin or theme developer? In almost all cases, nothing. Everything should continue to run as expected, and this should fix a number of hard-to-trace bugs when different plugins are stepping on each others callbacks.

Who is affected?

If your plugin directly accesses the $wp_filter global rather than using the public hooks API, you might run into compatibility issues.

Case 1: Directly setting callbacks in the $wp_filter array

$wp_filter['save_post'][10]['my_special_key'] = array( 'function' => 'my_callback_function', 'accepted_args' => 2 );

This will no longer work, and will cause a fatal error. $wp_filter['save_post'] is no longer a simple array. Rather, it is an object that implements the ArrayAccess interface.

You have two options to work around. The first (and preferred) method is to use the add_filter() or add_action() functions.

add_action( 'save_post', 'my_callback_function', 10, 2 );

If, for some reason, you absolutely cannot, you can still work around this.

if ( ! isset( $wp_filter[ 'save_post' ] ) ) {
	$wp_filter[ 'save_post' ] = new WP_Hook();
}
$wp_filter[ 'save_post' ]->callbacks[ 10 ][ 'my_special_key' ] = array( 'function' => 'my_callback_function', 'accepted_args' => 2 );

Case 2: Directly unsetting callbacks in the $wp_filter array

unset( $wp_filter[ 'save_post' ][ 10 ][ $my_callback_id ] );

This will fail for the same reason as case one. To work around, you can use the standard remove_filter() / remove_action() functions.

remove_action( 'save_post', 'my_callback_function', 10, 2 );

Or, if you absolutely must access the array directly:

if ( isset( $wp_filter[ 'save_post' ] ) ) {
	unset( $wp_filter[ 'save_post' ]->callbacks[ 10 ][ $my_callback_id ] );
}

Case 3: Checking if a hook is an array

if ( isset( $wp_filter[ 'save_post' ] ) && is_array( $wp_filter[ 'save_post' ] ) ) {
	// do something
}

This will always return false. $wp_filter[ 'save_post' ] is a WP_Hook object. To check if a hook has callbacks, use has_action() or has_filter().

if ( has_action( 'save_post' ) ) {
  // do something
}

Case 4: Moving the $wp_filter array pointer manually

If you're calling next() or prev() on the $wp_filter array pointer to manually manage the order that callbacks are called in (or if you’re doing it to work around #17817), you will likely be unsuccessful. Use callback priorities, add_action() / add_filter(), and remove_action() / remove_filter() appropriately and let WordPress manage execution order.

Other Cases

Almost all other cases where you might manipulate the $wp_filter global directly should continue to function as expected. The WP_Hook object implements the ArrayAccess and IteratorAggregate interfaces so that, while it’s not recommended, you may continue to iterate over callbacks in $wp_filter or directly retrieve priorities from the callback array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.