Use the $wp_filter global to inspect WordPress hooks, priorities, and callback sources when debugging actions and filters.
EN

How to List All Hooked Functions in WordPress

5.00 /5 - (30 votes )
Last verified: May 1, 2026
9min read
Guide
Full-stack developer

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?

Learn more about professional WordPress development at WPPoland. You need to know exactly what functions are attached to a specific hook (e.g., the_content or wp_head).

The fastest way to debug that is to inspect $wp_filter, list callbacks by priority, and trace whether each callback comes from core, a plugin, or the active theme.

#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.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you want to convert the article into a working site improvement, redesign, or build plan, I can define the scope and implement it.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Article FAQ

Frequently Asked Questions

Practical answers to apply the topic in real execution.

SEO-ready GEO-ready AEO-ready 3 Q&A
How do you inspect callbacks attached to a WordPress hook?
The usual route is checking the global $wp_filter structure and listing callbacks together with their priorities, then using reflection when you need file and line details.
Why are anonymous functions harder to debug?
Because closures do not expose a simple function name, so you often need reflection or deeper inspection to find their source.
What helps most when tracking a broken hook?
Knowing the hook name, callback priority, source file and whether the callback comes from the theme, WordPress core or a plugin.

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

Let’s discuss

Related Articles

Stop writing messy if-statements. Learn the difference between in_category and has_term, how to handle recursive child categories efficiently, and optimize your conditional tags.
development

WordPress conditional logic for taxonomies

Stop writing messy if-statements. Learn the difference between in_category and has_term, how to handle recursive child categories efficiently, and optimize your conditional tags.

Practical notes on WP_Query: when get_posts beats new WP_Query, why meta_query on unindexed keys collapses at scale, and how to paginate custom loops without hitting 404s.
development

A working guide to WP_Query and the loop (2026 performance edition)

Practical notes on WP_Query: when get_posts beats new WP_Query, why meta_query on unindexed keys collapses at scale, and how to paginate custom loops without hitting 404s.

has_term() and is_tax() are often confused. See the complete guide to conditional logic for categories, tags, and custom taxonomies.
development

Check if a post belongs to a taxonomy term

has_term() and is_tax() are often confused. See the complete guide to conditional logic for categories, tags, and custom taxonomies.