There has been little discussion on the use of PHP Closures with WordPress code, hopefully we will be able to spark some here! Before getting into the nitty-gritty, to make sure everyone is on the same page a quick explanation of a PHP Closure.
Anonymous functions, also known as closures, allow the creation of functions which have no specified name. They are most useful as the value of callback parameters, but they have many other uses.
php.net
Why Use Closures?
The ability to create single-use callback functions can be very useful when developing in WordPress land, as it’s very often one needs to run code via hooks and filters. At its simplest consider the following code:
function jh_excerpt_length() { | |
return 50; | |
} | |
add_filter( 'excerpt_length', 'jh_excerpt_length' ); |
A very basic example, but something seen often. Now the example using a PHP Closure:
add_filter( 'excerpt_length', function() { | |
return 50; | |
} ); |
With the Closure you are essentially passing a function object directly to add_filter()
that will be called when the hook fires. I prefer this for a couple of reasons.
- I don’t need to think of pointless names for functions that are only used for a specific hook (e.g.
jh_plugin_init()
). - The Closure is only one statement, so all the code is encapsulated into one block. When working on a large project, I don’t want 100 named functions all created at the “top level” of the code hierarchy, when in reality many functions only exist to be called once via other functions and hooks. I want the code to flow in the same way the model in my head appears. To illustrate this:
function jh_plugin_init() { | |
if ( function_exists( 'w4tc_cdn' ) ) | |
add_action( 'load-post-new.php', '_jh_add_parse_query_action' ); | |
} | |
add_action( 'init', 'jh_plugin_init' ); | |
function _jh_add_parse_query_action() { | |
add_action( 'parse_query', '_jh_add_query_var_to_parse_query' ); | |
} | |
function _jh_add_query_var_to_parse_query( $wp ) { | |
$wp->query_vars['post_type'] = 'page'; | |
} |
The above example is a little contrived, but it demonstrates how all the code becomes 1 dimensional. To me – the following fits better with how I think about code. The fact I need to use functions is a distraction, if I was not confined to hooks/filters I would not split the above into separate functions.
add_action( 'init', function() { | |
if ( ! function_exists( 'w4tc_cdn' ) ) | |
return; | |
add_action( 'load-post-new.php', function() { | |
add_action( 'parse_query', function( $wp ) { | |
$wp->query_vars['post_type'] = 'page'; | |
} ); | |
} ); | |
} ); |
To me this makes more sense when it comes to trying to find out what is happening and where. I am to be able to plug the code in where I want to run it, rather than having to sift though function declaration after declaration to see what’s running where. Named functions should be used for reusable code blocks, often I find most of the modifications made by hooks are not that.
Why not use Closures?
The majority of development work I do is on client projects, and other for-purpose applications rather than distributed plugins or themes. For me this means “get code hooked into WordPress where you need to, and make it do what you want it to do”. Plugin / theme development isn’t necessarily like that, you have considerations of third parties interacting with the code, you have to make sure you are not swallowing a hook'
s already altered output and provide ways for users to change how things work in a configurable way. This leads to when it’s not so great to use closures.
- Where anything is likely to want to remove a function you have created (you can’t use
remove_filter()
with a Closure, as there is no function name), as stated above – not great for distributed code. - Closures are PHP 5.3+ only, which means if you want to support all WordPress installs, they are a “no go”. However, anyone running PHP 5.2 should upgrade to 5.3 as it has numerous benefits over 5.2 in terms of performance and language features.
- Where you are likely to want to use the function multiple times, from different scopes.
- When you need the function to be accessible at all times (e.g.
wp_schedule_event()
callback.
As you can see Closures certainly aren’t the answer in many situations, but do have their uses. If they let you be more productive when coding and the environment you are working in allows it then I think we should embrace new language features like this. This is just some examples of how I use Closures with WordPress, I am looking forward to discussing the benefits / disadvantages with everyone below.