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.
I used a closure on my site and now it’s a white screen, Thanks very much for your great advice!
NOT.
Sounds like you are running PHP 3.0, I presume your sysops beardy man told you you need to run on Centos 1.1 because that’s secure 😉
Turns out it was an unrelated issue caused by the fact that I have 5 different related posts plugins active on my site.
I just released a minor plugin to the repository that uses these “closures” (BNS Bio) only to quickly find out that I will need to re-write (scheduled for Nov 20) them for optimal compatibility since a recent tweet I read indicated 66% of WordPress installations are running PHP 5.2
Otherwise, I would definitely be looking to make more uses of this functionality as I agree with most all of your discussion points.
Yeah, it’s a shame so many people are still running 5.2 which makes the plugins directory a no go for any recent PHP features.
I wrote the plugin more as a teaching aide for an upcoming Meetup presentation I am doing, thus the November 20 re-write … the WordPress repository seemed the better place of all the options to distribute from. Fortunately I already covered setting up a local test environment, so everyone should be using PHP 5.3+ in those.
Great roundup Joe.
This approach fits my thinking too but the alternative is a habit I’m only just starting to break. I’ve to use this approach the most when I have theme specific settings registered using the settings API that needs more validation logic than a simple sanitise eg:
add_settings_field( 'id', __( 'Field name' ), ... etc... );
register_setting( 'general', 'option_id', function( $value ) {
return array_filter( $value, function( $v ) {
return is_int( $v );
} );
} );
It feels a lot like javascript/jQuery in some ways which I really like.
I’m in 2 minds as to whether to use the approach for some client sites even though I have full control – part of our offering is that the code is GPL so they can take the code with them and that can mean it’ll break if they have crap hosting. Not a huge problem in reality though 🙂
I guess it depends on the job – if it’s likely to become complex through scope changes/feature requests then classes and helper classes are a more flexible and perhaps more maintainable approach to take.
Yeah, I use them a lot for PHP only stuff like array_map, usort, array-filter etc, so much better than doing string name callback!
For client work, we are generally pretty upfront about the fact they will need PHP 5.3. I certainly don’t want to be working on a long term project and be stuck with 5.2, especially when it’s not such a big deal to have them upgrade PHP. I don’t think there are many sounds arguments for running 5.2, maybe some LTS distros don’t have access to it yet.
Thanks for that Joe – I didn’t know closures were anonymous functions.
I’ve always found it a bit unorganised having an add_action or add_filter just somewhere in the script. Sometimes the hook is above a function, sometimes below – sometimes nowhere near it.
I think it adds much needed structure to the code and whereas they can’t always be used they’re definitely a step in the right direction for WP when it supports PHP 5.3.
Nice one Seph.
I’ve only ever known closures as anonymous functions, so that helped clear some things up.
The way I see it, it’s a lot nicer seeing it that way, it seems five times easier to read.
Great writeup Joe, thanks!
I’m generally against using anonymous functions but your post got me thinking again about it. Would you suggest then using closures for client projects and other for-purpose applications and avoiding them in Plugin / theme development?
I had no idea you could do this in php. Reminds me of javascript closures now.
There’s a problem here. You say effectively, but actually, the word literally would be more appropriate.
PHP 5.3 doesn’t support true closures, rather it’s a compiler trick. For example take this example:
$func = function($x){ return $x +1; }
Here you would expect that the function defined is a first class citizen and that $func is holding an anonymous function. Wrong.
Here, $func is actually an object of type Closure.
Both of these do the same thing:
$func(5);
$func->__invoke(5);
To make matters worse, anonymous functions still aren’t quite first class citizens in PHP 5.3, and a subtle distinction is missed
$anonymous_f = function(){ echo ‘anonymous’; }
$x = 5;
$closure = function() use($x) { $x += 5; }
A closure is an anonymous function that modifies things outside its definition. In PHP to use a true closure you must explicitly specify the variables that are going to be modified. Here $anonymous_f is not a closure, but a mere anonymous function.
http://php.net/manual/en/class.closure.php
Hi Tom,
> There’s a problem here. You say effectively, but actually, the word literally would be more appropriate.
I didn’t say effectively I said essentially, and as you point out, a function is not a first class object, it’s a Closure, so it’s not “literally” a function.
I think you are being over pedantic about the implementation of closures, there is little difference from the programmers perspective, regardless of the compiler’s decisions. You can create a “function” object, assign it to a variable and invoke the function as expected. You can also inherit variables from the parent scope – in most cases you don’t need more than that, and in 90% of cases it will work just fine.
Sure, it’s not as versatile as JavaScript closures with a different scope model, but it’s a sure improvement (or at least another string to the bow) on named functions and classes. I am sure you have been using PHP long enough to realize it doesn’t implement everything the way a lot of people would like.
I wouldn’t take it as criticism, I mention it because it points out some further bits that people may not know that leads to plenty of cool stuff like alternative implementations of iterators by swapping out the internal scope of an anonymous function =]
I copied out your example code in the picture and now my website says ‘syntax error, unexpected ‘}”, is it meant to do that?
I must admit that I never heard of php closures. Allthough hearing about it is really great for it seems to make some of my former dreams come true. It would be a great thing to reduce a lot bunch of functions that only return a single value and so on (as mentioned in the article). As long as you work only for clients and not for theme parks like themeforest etc it is okay.
Pitching in, closures should be very carefully considered before they are implemented as they effectively disable the remove_action/filter functions. I think closures can be short-sighted and somewhat of a ‘cheat’. But I can also see a few valid cases where they are handy and harmless.