wppaste
WordPress

remove_filter( string $hook_name, callable|string|array $callback, int $priority = 10 ): bool

Since
1.2.0
Source
wp-includes/plugin.php:318

Detaches a previously registered callback from a filter hook, using the hook name, callback, and priority to identify it. The $priority argument must match the value used when the callback was originally added via add_filter(), or the removal silently fails and the callback keeps running. Pair it with has_filter() first if you need to know whether a callback is even attached before trying to remove it.

Removes a callback function from a filter hook.

Description

This can be used to remove default functions attached to a specific filter hook and possibly replace them with a substitute.

To remove a hook, the $callback and $priority arguments must match when the hook was added. This goes for both filters and actions. No warning will be given on removal failure.

Compatibility

WordPress
since 1.2.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$hook_namestring
The filter hook to which the function to be removed is hooked.
$callbackcallable|string|array
The callback to be removed from running when the filter is applied.
This function can be called unconditionally to speculatively remove a callback that may or may not exist.
$priorityintoptional
The exact priority used when adding the original filter callback. Default 10.Default: 10

Return value

bool
Whether the function existed before it was removed.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

Strip wpautop from post content before displaying it

Compare the_content output with and without the core wpautop filter attached.

$post = get_post( 1 );

$before = apply_filters( 'the_content', $post->post_content );

remove_filter( 'the_content', 'wpautop' );

$after = apply_filters( 'the_content', $post->post_content );

echo 'With wpautop: ' . esc_html( $before );
echo '<br>';
echo 'Without wpautop: ' . esc_html( $after );

wpautop is normally attached to the_content at priority 10, which is why no explicit $priority argument is needed here.

Show that removal fails silently when the priority doesn't match

Attach a title filter at priority 20, then attempt to remove it with the wrong priority before removing it correctly.

function wpp_shout_title( $title ) {
	return strtoupper( $title );
}

add_filter( 'the_title', 'wpp_shout_title', 20 );

$post = get_post( 1 );

echo 'Before removal: ' . esc_html( apply_filters( 'the_title', $post->post_title, $post->ID ) );
echo '<br>';

$wrong = remove_filter( 'the_title', 'wpp_shout_title', 10 );
echo 'remove_filter() with priority 10 returned: ' . ( $wrong ? 'true' : 'false' );
echo '<br>';
echo 'Still shouting: ' . esc_html( apply_filters( 'the_title', $post->post_title, $post->ID ) );
echo '<br>';

$right = remove_filter( 'the_title', 'wpp_shout_title', 20 );
echo 'remove_filter() with priority 20 returned: ' . ( $right ? 'true' : 'false' );
echo '<br>';
echo 'After correct removal: ' . esc_html( apply_filters( 'the_title', $post->post_title, $post->ID ) );

Common problems and fixes · 3

Why does remove_filter() return false even though I definitely added the callback?

remove_filter() delegates to the hook's internal remove_filter() method, which only matches a callback when both $callback and $priority are identical to what was passed to add_filter(). A hook added at priority 20 is invisible to a removal call using the default priority 10.

Why doesn't removing a filter from inside a callback on that same hook change the current output?

When a hook is already being iterated by apply_filters(), the running pass uses the list of callbacks captured at the start of that call. Calling remove_filter() mid-run updates the hook's stored callbacks for future calls, but not the iteration already in progress.

Why can't I remove a filter that was added with an anonymous function?

remove_filter() matches callbacks by identity (the same string, array, or object reference used in add_filter()). A closure created fresh at removal time is a different object in memory, even if its code looks identical, so WordPress has nothing to match it against.

Alternatives and related functions

remove_action
When the hook was registered with add_action() and fired with do_action() rather than apply_filters(), since actions and filters share the same underlying hook storage but have their own paired functions.
has_filter
When you only need to check whether a callback is currently attached to a hook, optionally at a specific priority, without removing it.
remove_all_filters
When you want to strip every callback (or every callback at a given priority) from a hook instead of removing one specific callback.
add_filter
When you need to reattach a replacement callback to the same hook right after removing the original one.

Performance profile

How much work a call to remove_filter() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Trivial

Touches nothing outside its own arguments.

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
8–19

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 19.

Plugin surface
None

Nothing here hands control to plugin code.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What one call costs · 2 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost remove_filter() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
!isset($wp_filter[$hook_name])8none
isset($wp_filter[$hook_name])18–19->remove_filter()

Across PHP versions

Compiles the same on PHP 7.4, 8.1, 8.2, 8.3, 8.4, 8.5 and 8.6-dev: 19 instructions, 8–19 executed per call, 2 branches. The work does not change between versions.

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Used by · 50

Show all 50

Source code

function remove_filter( $hook_name, $callback, $priority = 10 ) {	global $wp_filter; 	$r = false; 	if ( isset( $wp_filter[ $hook_name ] ) ) {		$r = $wp_filter[ $hook_name ]->remove_filter( $hook_name, $callback, $priority ); 		if ( ! $wp_filter[ $hook_name ]->callbacks ) {			unset( $wp_filter[ $hook_name ] );		}	} 	return $r;}

Changelog

Introduced in 1.2.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

About this page

Parsed data
Generated from the wordpress-develop 7.0.4 tag, from src/wp-includes/plugin.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.