Use $wp_filter global variable to debug actions and filters. See priorities and callback functions instantly.
EN

How to debug WordPress hooks? (List all actions/Filters)

5.00 /5 - (30 votes )
Last verified: March 1, 2026
Experience: 5+ years experience
Table of Contents

WordPress runs on Hooks (Actions and Filters). Sometimes, unexpected things happen – content disappears, titles change, styles break. You suspect a plugin is interfering, but which one?

You need to know exactly what functions are attached to a specific hook (e.g., the_content or wp_head).

The WordPress hook system: Understanding the foundation

WordPress’s hook system is what makes it extensible. Every major function in WordPress fires hooks, allowing plugins and themes to modify behavior without editing core files.

Two Types of Hooks:

  1. Actions – Do something at a specific point (e.g., wp_head, the_content)
  2. Filters – Modify data before it’s used (e.g., the_title, the_content)

When debugging, you need to see:

  • What functions are hooked
  • Their priorities (execution order)
  • Which plugin/theme added them
  • The callback function names

The complete debugging snippet

Add this enhanced function to your functions.php or use it in a Must-Use plugin during development:

/**
 * Inspect WordPress hooks with detailed information
 *
 * @param string $hook_name The hook to inspect (e.g., 'the_content', 'wp_head')
 * @param bool $show_details Show full callback details
 * @return void
 */
function wppoland_inspect_hook( $hook_name, $show_details = false ) {
    global $wp_filter;

    if ( ! isset( $wp_filter[ $hook_name ] ) ) {
        echo '<div style="background:#fff3cd; border:2px solid #ffc107; padding:15px; margin:20px 0;">';
        echo "<strong>Hook '$hook_name' has no attached functions.</strong>";
        echo '</div>';
        return;
    }

    $hook_data = $wp_filter[ $hook_name ];

    echo '<div style="background:#fff; border:2px solid #dc3545; padding:20px; margin:20px 0; font-family:monospace; font-size:12px; max-width:100%; overflow-x:auto;">';
    echo "<h2 style='margin-top:0; color:#dc3545;'>Debugging Hook: <code>$hook_name</code></h2>";

    // Sort by priority
    ksort( $hook_data->callbacks );

    echo '<table style="width:100%; border-collapse:collapse;">';
    echo '<thead><tr style="background:#f8f9fa;"><th style="padding:8px; text-align:left; border:1px solid #dee2e6;">Priority</th><th style="padding:8px; text-align:left; border:1px solid #dee2e6;">Function</th><th style="padding:8px; text-align:left; border:1px solid #dee2e6;">Source</th></tr></thead>';
    echo '<tbody>';

    foreach ( $hook_data->callbacks as $priority => $callbacks ) {
        foreach ( $callbacks as $callback ) {
            $function_name = 'Unknown';
            $source_file = 'Unknown';

            if ( is_string( $callback['function'] ) ) {
                $function_name = $callback['function'];
                if ( function_exists( $function_name ) ) {
                    $reflection = new ReflectionFunction( $function_name );
                    $source_file = $reflection->getFileName() . ':' . $reflection->getStartLine();
                }
            } elseif ( is_array( $callback['function'] ) ) {
                if ( is_object( $callback['function'][0] ) ) {
                    $function_name = get_class( $callback['function'][0] ) . '::' . $callback['function'][1];
                } else {
                    $function_name = $callback['function'][0] . '::' . $callback['function'][1];
                }

                try {
                    $reflection = new ReflectionMethod( $callback['function'][0], $callback['function'][1] );
                    $source_file = $reflection->getFileName() . ':' . $reflection->getStartLine();
                } catch ( ReflectionException $e ) {
                    $source_file = 'Unable to determine';
                }
            } elseif ( is_object( $callback['function'] ) ) {
                $function_name = 'Closure';
                $reflection = new ReflectionFunction( $callback['function'] );
                $source_file = $reflection->getFileName() . ':' . $reflection->getStartLine();
            }

            // Detect plugin/theme
            $source_type = 'Core';
            if ( strpos( $source_file, 'wp-content/plugins' ) !== false ) {
                $source_type = 'Plugin';
                preg_match( '/plugins\/([^\/]+)/', $source_file, $matches );
                $plugin_name = isset( $matches[1] ) ? $matches[1] : 'Unknown';
            } elseif ( strpos( $source_file, 'wp-content/themes' ) !== false ) {
                $source_type = 'Theme';
                preg_match( '/themes\/([^\/]+)/', $source_file, $matches );
                $plugin_name = isset( $matches[1] ) ? $matches[1] : 'Unknown';
            } else {
                $plugin_name = 'WordPress';
            }

            echo '<tr style="border-bottom:1px solid #dee2e6;">';
            echo '<td style="padding:8px; border:1px solid #dee2e6;"><strong>' . esc_html( $priority ) . '</strong></td>';
            echo '<td style="padding:8px; border:1px solid #dee2e6;"><code>' . esc_html( $function_name ) . '</code></td>';
            echo '<td style="padding:8px; border:1px solid #dee2e6;"><span style="color:' . ( $source_type === 'Plugin' ? '#dc3545' : ( $source_type === 'Theme' ? '#0073aa' : '#28a745' ) ) . ';">' . esc_html( $source_type ) . '</span> - ' . esc_html( $plugin_name ) . '</td>';
            echo '</tr>';

            if ( $show_details ) {
                echo '<tr><td colspan="3" style="padding:4px 8px; font-size:11px; color:#666; border:1px solid #dee2e6;">';
                echo 'File: ' . esc_html( $source_file );
                echo '</td></tr>';
            }
        }
    }

    echo '</tbody></table>';
    echo '</div>';
}

// Usage Examples:
// add_action( 'wp_footer', function(){ wppoland_inspect_hook('the_content'); } );
// add_action( 'wp_footer', function(){ wppoland_inspect_hook('wp_head', true); } ); // With details

How to use the debugging function

Basic usage

Add this to your functions.php temporarily:

// Inspect the_content hook
add_action( 'wp_footer', function() {
    if ( current_user_can( 'manage_options' ) ) { // Only for admins
        wppoland_inspect_hook( 'the_content' );
    }
} );

Visit any page and scroll to the footer. You’ll see a detailed table showing all functions hooked to the_content.

Inspect multiple hooks

add_action( 'wp_footer', function() {
    if ( ! current_user_can( 'manage_options' ) ) return;

    $hooks_to_check = array( 'the_content', 'wp_head', 'the_title', 'excerpt_length' );

    foreach ( $hooks_to_check as $hook ) {
        wppoland_inspect_hook( $hook );
    }
} );

Inspect with full details

// Show file paths and line numbers
add_action( 'wp_footer', function() {
    if ( current_user_can( 'manage_options' ) ) {
        wppoland_inspect_hook( 'the_content', true ); // true = show details
    }
} );

Understanding the output

The output shows a table with three columns:

Priority column

WordPress executes hooks in priority order (lower numbers first). Default priority is 10.

Common Priorities:

  • 1-9: Early execution (before default)
  • 10: Default priority
  • 11-99: Late execution (after default)
  • 999: Very late (almost last)

Example:

Priority 5:  wpautop (adds paragraphs)
Priority 10: do_shortcode (processes shortcodes)
Priority 20: custom_plugin_function (runs last)

Function column

Shows the callback function name:

  • String functions: wpautop, do_shortcode
  • Class methods: MyPlugin::process_content
  • Closures: Closure (anonymous functions)

Source column

Indicates where the hook was added:

  • Core: WordPress core functions
  • Plugin: Name of the plugin
  • Theme: Name of the theme

Common debugging scenarios

Scenario 1: Content disappears

Problem: Post content is empty or missing.

Debug:

wppoland_inspect_hook( 'the_content' );

Look for:

  • Functions that return empty strings
  • Functions with high priority that might override content
  • Plugin functions that strip HTML

Scenario 2: Title changes unexpectedly

Problem: Page titles are modified by something.

Debug:

wppoland_inspect_hook( 'the_title' );

Look for:

  • SEO plugins modifying titles
  • Translation plugins
  • Custom title filters

Scenario 3: Styles break

Problem: CSS/JS not loading correctly.

Debug:

wppoland_inspect_hook( 'wp_head' );
wppoland_inspect_hook( 'wp_enqueue_scripts' );

Look for:

  • Plugins removing stylesheets
  • Conflicting enqueue priorities
  • Scripts loading in wrong order

Advanced: Programmatic hook inspection

Check if specific function is hooked

function wppoland_is_function_hooked( $hook_name, $function_name ) {
    global $wp_filter;

    if ( ! isset( $wp_filter[ $hook_name ] ) ) {
        return false;
    }

    foreach ( $wp_filter[ $hook_name ]->callbacks as $priority => $callbacks ) {
        foreach ( $callbacks as $callback ) {
            if ( is_string( $callback['function'] ) && $callback['function'] === $function_name ) {
                return array( 'priority' => $priority, 'found' => true );
            }
            if ( is_array( $callback['function'] ) && $callback['function'][1] === $function_name ) {
                return array( 'priority' => $priority, 'found' => true );
            }
        }
    }

    return false;
}

// Usage
if ( wppoland_is_function_hooked( 'the_content', 'wpautop' ) ) {
    echo 'wpautop is hooked to the_content';
}

Remove specific hook temporarily

// Remove a problematic hook
remove_filter( 'the_content', 'problematic_function', 10 );

// Or remove all hooks from a plugin
global $wp_filter;
if ( isset( $wp_filter['the_content'] ) ) {
    foreach ( $wp_filter['the_content']->callbacks as $priority => $callbacks ) {
        foreach ( $callbacks as $callback ) {
            if ( strpos( $callback['function'], 'ProblemPlugin' ) !== false ) {
                remove_filter( 'the_content', $callback['function'], $priority );
            }
        }
    }
}

Modern alternative: Query monitor plugin

In 2026, the best way to debug hooks is using the Query Monitor plugin by John Blackbourn.

Why query monitor?

  1. Visual Interface: Clean, searchable table
  2. File Paths: Shows exact file locations
  3. Component Names: Identifies plugins/themes
  4. Performance Data: Shows execution time per hook
  5. No Code Required: GUI-based debugging

Installation

## Via wp-CLI
wp plugin install query-monitor --activate

## Or download from WordPress.org

Using query monitor

  1. Install and activate Query Monitor
  2. Visit any page on your site
  3. Look for the “QM” icon in the admin bar
  4. Click “Hooks & Actions”
  5. Search for your hook name
  6. See all attached functions with priorities and sources

Query monitor vs. Custom snippet

Use Query Monitor when:

  • You need a permanent debugging solution
  • You want performance metrics
  • You’re debugging multiple hooks
  • You prefer GUI over code

Use Custom Snippet when:

  • You need quick, one-off debugging
  • You’re in a development environment
  • You want to programmatically check hooks
  • Query Monitor isn’t available

Best practices for hook debugging

1. Only debug IN development

Never leave debugging code in production:

if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
    add_action( 'wp_footer', function() {
        wppoland_inspect_hook( 'the_content' );
    } );
}

2. Restrict to admins

Always check user capabilities:

if ( current_user_can( 'manage_options' ) ) {
    // Show debug info
}

3. Use descriptive hook names

When creating custom hooks, use prefixes:

// Good
do_action( 'wppoland_before_content' );

// Bad
do_action( 'before_content' );

4. Document your hooks

/**
 * Fires before the main content area
 *
 * @param string $content The post content
 */
do_action( 'wppoland_before_content', $content );

Troubleshooting common issues

Issue: Hook not showing up

Possible Causes:

  1. Hook hasn’t fired yet (check hook timing)
  2. Conditional logic prevents hook from firing
  3. Hook name is misspelled

Solution:

// Check if hook exists
if ( has_action( 'your_hook_name' ) ) {
    echo 'Hook exists';
} else {
    echo 'Hook does not exist';
}

Issue: Function not executing

Possible Causes:

  1. Priority conflict
  2. Conditional logic
  3. Hook removed by another plugin

Solution:

// Check current priority
$priority = has_filter( 'the_content', 'your_function' );
echo "Priority: $priority";

Summary

Debugging WordPress hooks is essential for understanding plugin conflicts and theme issues. The custom snippet provides detailed information about:

  • What functions are hooked
  • Their execution priorities
  • Which plugins/themes added them
  • Source file locations

Key Takeaways:

  • Use $wp_filter global to inspect hooks
  • Query Monitor is the best GUI solution for 2026
  • Always restrict debugging to development/admin users
  • Understand priorities to fix execution order issues
  • Document your custom hooks for maintainability

Whether you use the custom snippet or Query Monitor, understanding WordPress hooks is crucial for professional WordPress development in 2026.

What should you know about How to debug WordPress hooks? (List all actions/Filters)?
How to debug WordPress hooks? (List all actions/Filters) is an essential aspect of WordPress website management that helps improve site performance, security, and user experience.
How does How to debug WordPress hooks? (List all actions/Filters) work?
How to debug WordPress hooks? (List all actions/Filters) involves configuring various settings and implementing best practices to optimize your WordPress website.
Why is How to debug WordPress hooks? (List all actions/Filters) important for WordPress?
How to debug WordPress hooks? (List all actions/Filters) is crucial because it directly impacts your website's search engine rankings, loading speed, and overall success.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles