Skip to content

Instantly share code, notes, and snippets.

@jbrinley
Created February 18, 2015 20:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbrinley/eaaad00b52e1316c6904 to your computer and use it in GitHub Desktop.
Save jbrinley/eaaad00b52e1316c6904 to your computer and use it in GitHub Desktop.
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