10

It seems a lot of plugin developers take the time to add filter/action hooks to let users tweak their products’ functionality. Which is great, but what they often don’t do is provide a list of hooks and how many arguments they take.

Has anyone found the best automated way to point at a plugin (or theme) directory and see a list of all available hooks?

I’ve seem some plugins that scan for hooks, but as far as I can tell, they show you which ones are actually being called to render a given page. Which I get can be handy. But sometimes if I know I’m interacting with a particular plugin, I want to know every place it might let me hook an action or filter.

So what I’m really looking for is something that, given a plugin root directory will create a list where each item includes:

  • tag
  • type (action or filter)
  • number of arguments
  • where it’s called (via do_action() or apply_filter()) in the source

A script would be great since this could presumably nicely HTMLify the whole thing and show it to me right in the admin UI for every plugin. But even a command-line script that outputs a useful static file would be great.

  • so what is the question? if you look for a plugin recommendation then it is off-topic here – Mark Kaplun Oct 6 '15 at 6:57
  • Sorry, don’t want to get too far into the WordPress Development Meta weeds, but a) I’m new here so I didn’t realize that asking for plugin recommendations were OT. I might have… some opinions… on that, but still, I should have realized that first. b) OTOH, I’m just trying to find any solution to my question, whether an existing plugin or shell script, or something from scratch. So the question is strictly a plugin recommendation request! – yonatron Oct 7 '15 at 19:26
  • well, seems like everybody else is having fun so no harm done. My "objection" to the question was actually more about it being a text parsing question which is intresting for people that like to write compiler style software but has very little to do with actual wordpress coding. For the record even questions which are about wordpress.org like how to submit a plugin will usually get voted as off-topic. – Mark Kaplun Oct 8 '15 at 3:02
6

There is no script or plugin that I know of to do what you want. As you have stated, there are scripts (even global variables) which you can use to print filters and actions currently being used.

As for dormant filters and actions, I have written two very basic functions (with some help here and there) which finds all apply_filters and do_action instances in a file and then prints it out

BASICS

  • We will use the RecursiveDirectoryIterator,RecursiveIteratorIterator and RegexIterator PHP classes to get all the PHP files within a directory. As example, on my localhost, I have used E:\xammp\htdocs\wordpress\wp-includes

  • We will then loop through the files, and search and return (preg_match_all) all instances of apply_filters and do_action. I have set it up to match nested instances of parenthesis and also to match possible whitespaces between apply_filters/do_action and the first parenthesis

We will simple then create an array with all filters and actions and then loop through the array and output the file name and filters and actions. We will skip files without filters/actions

IMPORTANT NOTES

  • This functions are very expensive. Run them only on a local test installation.

  • Modify the functions as needed. You can decide to write the output to a file, create a special backend page for that, the options are unlimited

OPTION 1

The first options function is very simple, we will return the contents of a file as a string using file_get_contents, search for the apply_filters/do_action instances and simply output the filename and filter/action names

I have commented the code for easy following

function get_all_filters_and_actions( $path = '' )
{
    //Check if we have a path, if not, return false
    if ( !$path ) 
        return false;

    // Validate and sanitize path
    $path = filter_var( $path, FILTER_SANITIZE_URL );
    /**
     * If valiadtion fails, return false
     *
     * You can add an error message of something here to tell
     * the user that the URL validation failed
     */
    if ( !$path ) 
        return false;

    // Get each php file from the directory or URL  
    $dir   = new RecursiveDirectoryIterator( $path );
    $flat  = new RecursiveIteratorIterator( $dir );
    $files = new RegexIterator( $flat, '/\.php$/i' );

    if ( $files ) {

        $output = '';
        foreach($files as $name=>$file) {
            /**
             * Match and return all instances of apply_filters(**) or do_action(**)
             * The regex will match the following
             * - Any depth of nesting of parentheses, so apply_filters( 'filter_name', parameter( 1,2 ) ) will be matched
             * - Whitespaces that might exist between apply_filters or do_action and the first parentheses
             */
            // Use file_get_contents to get contents of the php file
            $get_file_content =  file_get_contents( $file );
            // Use htmlspecialchars() to avoid HTML in filters from rendering in page
            $save_content = htmlspecialchars( $get_file_content );
            preg_match_all( '/(apply_filters|do_action)\s*(\([^()]*(?:(?-1)[^()]*)*+\))/', $save_content, $matches );

            // Build an array to hold the file name as key and apply_filters/do_action values as value
            if ( $matches[0] )
                $array[$name] = $matches[0];
        }
        foreach ( $array as $file_name=>$value ) {

            $output .= '<ul>';
                $output .= '<strong>File Path: ' . $file_name .'</strong></br>';
                $output .= 'The following filters and/or actions are available';
                foreach ( $value as $k=>$v ) {
                    $output .= '<li>' . $v . '</li>';
                }
            $output .= '</ul>';
        }
        return $output;
    }

    return false;
}

You can use at follow on a template, frontend or backend

echo get_all_filters_and_actions( 'E:\xammp\htdocs\wordpress\wp-includes' );

This will print

enter image description here

OPTION 2

This option is a bit more expensive to run. This function returns the line number where the filter/action can be found.

Here we use file to explode the file into an array, then we search and return the filter/action and the line number

function get_all_filters_and_actions2( $path = '' )
{
    //Check if we have a path, if not, return false
    if ( !$path ) 
        return false;

    // Validate and sanitize path
    $path = filter_var( $path, FILTER_SANITIZE_URL );
    /**
     * If valiadtion fails, return false
     *
     * You can add an error message of something here to tell
     * the user that the URL validation failed
     */
    if ( !$path ) 
        return false;

    // Get each php file from the directory or URL  
    $dir   = new RecursiveDirectoryIterator( $path );
    $flat  = new RecursiveIteratorIterator( $dir );
    $files = new RegexIterator( $flat, '/\.php$/i' );

    if ( $files ) {

        $output = '';
        $array  = [];
        foreach($files as $name=>$file) {
            /**
             * Match and return all instances of apply_filters(**) or do_action(**)
             * The regex will match the following
             * - Any depth of nesting of parentheses, so apply_filters( 'filter_name', parameter( 1,2 ) ) will be matched
             * - Whitespaces that might exist between apply_filters or do_action and the first parentheses
             */
            // Use file_get_contents to get contents of the php file
            $get_file_contents =  file( $file );
            foreach ( $get_file_contents as  $key=>$get_file_content ) {
                preg_match_all( '/(apply_filters|do_action)\s*(\([^()]*(?:(?-1)[^()]*)*+\))/', $get_file_content, $matches );

                if ( $matches[0] )
                    $array[$name][$key+1] = $matches[0];
            }
        }

        if ( $array ) {
            foreach ( $array as $file_name=>$values ) {
                $output .= '<ul>';
                    $output .= '<strong>File Path: ' . $file_name .'</strong></br>';
                    $output .= 'The following filters and/or actions are available';

                    foreach ( $values as $line_number=>$string ) {
                        $whitespaces = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
                        $output .= '<li>Line reference ' . $line_number . $whitespaces . $string[0] . '</li>';
                    }
                $output .= '</ul>';
            }
        }
        return $output;

    }

    return false;
}

You can use at follow on a template, frontend or backend

echo get_all_filters_and_actions2( 'E:\xammp\htdocs\wordpress\wp-includes' );

This will print

enter image description here

EDIT

This is basically as much as I can do without the scripts timing out or running out of memory. With the code in option 2, it is as easy as going to the said file and said line in the source code and then get all the valid parameter values of the filter/action, also, importantly, get the function and further context in which the filter/action is used

  • 1
    you have too much free time ;) but in the end it is never enough to know what are the filters but also what are the expected parameter values and results and this can not be achieved from the source without bactracking to get the doc block in case of core files and probably not available at all in non core plugins and themes. – Mark Kaplun Oct 7 '15 at 10:34
  • 1
    @MarkKaplun this is just something I quickly worked on :-). The functions above at least tell you where filters and actions are in specific plugin/theme/core directories. It still stay very important to go back to the source and make sure you understand what a specific filter do specifically. Some plugins and themes are poorly documented, so you still would need some kind of knowledge and background to know or figure out what a specific filter do in a specific function ;-) – Pieter Goosen Oct 7 '15 at 10:48
  • @PieterGoosen Yeah I’m usually ok with jumping back in to look at the source. If I’m doing this in the first place it means the plugin has hooks but not necessarily doc blocks for them. But if I take a look at where they’re actually used it helps me make sense of whether they’re valuable. Anyway these look like they could be great. I might even modify them 2nd to just write to an HTML file because then I could just stick the function declaration in an MU Plugin on the local server and run it from WP CLI. – yonatron Oct 7 '15 at 14:46
  • @yonatron Glad that my two cents helps in some way. If you ever to come to write your own modification from my code, you can always add your code and explanation as answer (which will be great) ;-). Enjoy – Pieter Goosen Oct 7 '15 at 14:54
  • 1
    @PieterGoosen how much time you took to write this script as well as to document, gosh its awesome ***** :) – Webloper Oct 7 '15 at 17:21
6

Sounds like WP Parser does what you are looking for. It is used to generate the official developer reference. It lists parameters, @since tags and references to the source. It works with all WordPress plugins and can be accessed via command line:

wp parser create /path/to/source/code --user=<id|login>
  • 1
    I'm not very familiar with that script, but it seems it needs a filter or action to be well documented. Would appreciate feedback on this from @Rarst ;-) – Pieter Goosen Oct 7 '15 at 11:07
  • I haven’t tried it yet, but based on the description, I think Pieter’s concern is on target. I want to find all the hooks, not just the ones preceded by well formatted doc blocks. I think folks who take the time to comment their hooks/functions using WordPress conventions are already running this kind of script and publishing API references on their support sites. I realize there’s some risk inherent in this, since if a developer doesn’t publicly document a filter, it could get changed/deprecated without warning, but for some plugins I use, I can’t wait around for docs to show up online. – yonatron Oct 7 '15 at 14:57
  • It also parses undocumented hooks. Example: developer.wordpress.org/reference/hooks/graceful_fail – Jan Beck Oct 7 '15 at 22:01
3

The Fast and the Furious

The good ol' *nix command-line is always handy:

# grep  --line-number                                         \
        --exclude-dir=/path/to/some/directory                 \
        --include=*.php                                       \ 
        --recursive                                           \
        "add_filter\|do_action\|apply_filters"                \
        /path/to/wp-content/plugins/some-plugin               \ 
 | less

Many more options via #man grep.

Then we can even create a simple bash script wp-search.sh:

#!/bash/bin
grep --line-number                            \
    --exclude-dir=/path/to/some/directory     \
    --include=*.$1                            \
    --recursive $2 $3

and run it with.

 # bash wp-search.sh php "add_filter\|do_action\|apply_filters" /path/to/some-plugin

Pretty output

We can use --color attribute to colorize the output of grep, but note that it will not work with less.

Another option would be to generate an HTML table for the search results.

Here's an awk example I constructed that outputs the search results as an HTML table, into the results.html file:

  | sed 's/:/: /2' \
  | awk ' \
        BEGIN { \
            print "<table><tr><th>Results</th><th>Location</th></tr>"  \
        } \
        { \
            $1=$1; location=$1; $1=""; print "<tr><td>" $0 "</td><td>" location "</td><tr>" \
        } \
        END {  \
           print "</table>" \
       }' \
 > results.html

where I used this trick to remove all leading white space and this one to print all fields but the first one.

I use sed here just to add extra space after the second colon (:), just in case there's no space there.

Script

We could add this to our wp-search.sh script:

#!/bash/bin
grep   --with-filename \
       --line-number \
       --exclude-dir=/path/to/some/directory \
       --include=*.$1 \
       --recursive $2 $3 \
| sed 's/:/: /2' \
| awk ' BEGIN { \
        print "<table><tr><th>Results</th><th>Location</th></tr>"  \
    } \
    { \
        $1=$1; location=$1; $1=""; print "<tr><td>" $0 "</td><td>" location "</td><tr>" \
    } \
    END {  \
        print "</table>" \
    }' \
> /path/to/results.html

where you have to adjust the /path/to/some/directory and /path/to/results.html to your needs.

Example - Searching a plugin

If we try this on the wordpress-importer plugin with:

bash wp-search.sh php "add_filter\|do_action" /path/to/wp-content/plugins/wordpress-importer/

then the results.html file will display as:

results

Example - Searching the core

I time tested it for the core:

time bash wp-search.sh php "add_filter\|do_action" /path/to/wordpress/core/

real    0m0.083s
user    0m0.067s
sys     0m0.017s

and it's fast!

Notes

To get extra context we might use the -C NUMBER of grep.

We could modify the HTML output in various ways, but hopefully you can adjust this further to your needs.

  • Thanks! I use grep in a pinch but I’m such a novice at shell stuff that I never even got round to using the pipes for OR. The other thing I’d want to add though are options to pretty print it a bit. The output of the above grep line is still going to be pretty hard to skim. – yonatron Oct 7 '15 at 14:32
  • 1
    I updated the answer with an example of pretty print @yonatron – birgire Oct 8 '15 at 9:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.