Reflection on filters

Filters are the most awesome thing about WordPress, code wise. 1

The Problem

Passing a class method to a filter, however, is not so awesome.

Say you have this class for manipulating WP_Query:

class My_Plugin {

    function parse_query( $wp_query ) {
        ...
    }

    function posts_clauses( $clauses, $wp_query ) {
        ...
    }

    function the_posts( $posts, $wp_query ) {
        ...
    }
}

To hook it up to the appropriate filters, you have to write this code:

add_action( 'parse_query', array( 'My_Plugin', 'parse_query' ) );
add_filter( 'posts_clauses', array( 'My_Plugin', 'posts_clauses' ), 10, 2 );
add_filter( 'the_posts', array( 'My_Plugin', 'the_posts' ), 10, 2 );

Ugh… so much duplication! What a wonderful world it would be if we didn’t have to write all that boilerplate code each time.

The Solution

PHP has this neat feature called reflection. With it, you can reverse-engineer the properties and methods of a class or object.

So, this is exactly what the scbHooks class does. Given a class or object, it hooks all the public methods to filters of the same name. It also passes the appropriate number of arguments, by looking at the method signature.

So, instead of the boilerplate code above, you would just write:

scbHooks::add( 'My_Plugin' );

Ah… DRYness restored.

If you change your mind, you can remove the filters, just as easily:

scbHooks::remove( 'My_Plugin' );

Limitations

That’s pretty swell, but there are some drawbacks:

I use scbHooks more like a starter kit, with which I can rapidly prototype a solution. Once I hit one of the above limitations, I abandon ship:

scbHooks::debug( 'My_Plugin' );

This method will actually generate the boilerplate source code, which you can paste into the plugin and completely forget about scbHooks.

The Extended Solution

Yitzi, a commenter, had the fabulous idea to set the priority via doc comment. I just modified the syntax a little and also added the ability to change the filter name:

class My_Plugin {

    /**
     * @hook: wp_ajax_my-action
     * @priority: 11
     */
    function ajax_handler() {
        // awesome code
    }
}

scbHooks::debug( 'My_Plugin' );

// Result: add_filter( 'wp_ajax_my-action',  array( 'My_Plugin', 'ajax_handler' ), 11, 0 );

This effectively solves all the drawbacks I mentioned earlier. As such, I don’t think I’ll be abandoning ship so soon after all.

The Code

By the way, scbHooks is now a part of scbFramework.

  1. Actions are just filters in disguise.

comments powered by Disqus