If you worked for even just a little while with WordPress, then you must know by now that action and filter hooks are the cornerstone of its extensibility. Through the Plugin API, and using functions such as add_action()
, do_action()
, add_filter()
and apply_filters()
we can build almost any thing we can think of on top of the core, often in the form of plugins and themes, but there are still problems that can appear when we implement them.
One of these problems has to do with how much control we have over the functions and class methods that we add to actions and filters. Functions like do_action()
and apply_filters()
use call_user_func_array()
internally, which is a native PHP function that allows to call a function (or class method) dynamically.
The functions called using call_user_func_array()
should be publicly accessible. That’s pretty obvious for functions, since they are always public and you can use them everywhere once they have been declared, but class methods need to be public too in order to work with call_user_func_array()
. That shouldn’t be a problem by itself, but sometimes you may need some functions and methods to not be used outside of the scope of the action or filter that you hooked them too.
Just imagine that you have a plugin of your own, which has a function that executes some kind of heavy process against the database. This function is hooked to init
, and you don’t call it again during the runtime of your plugin.
Now, let’s say that other developer uses your plugin and intentionally or accidentally calls your function in a different scope, causing some kind of havoc in his implementation. Or maybe the developer has access to internal information of your plugin that shouldn’t be used or modified at that point. There are a number of possible issues with this, and it’s pointless to cite them all to get the picture.
Yes, you can say that’s not really your problem, because your plugin by itself doesn’t have any issues with that function, but you can always take an extra measure to avoid errors or improve security for others. The problem is that, in theory, nothing prevents the usage of functions and public methods. Of course, you can always leave some kind of documentation in your function or method indicating that when and where it should be used. In fact, there are some kind-of accepted standards to be applied to functions to indicate if they shouldn’t be used, such as the @internal
tag and the _
prefix in its name. But you can never be sure that other developers would read the function documentation, or even care.
A quick way to limit the usage of functions and class methods is using the current_filter()
and current_action()
functions. By just checking if the filter or action that is being currently executed is the one we want, and throwing some kind of error or message (or just returning) if not, we can achieve some kind of limitation to the usage that we want to allow to certain parts of our code.
Another way to restrict the usage of a function would be using did_action()
. This way you can check if a given point during the runtime has already being executed and, if so, prevent your default process from running. You can even get a similar result using a static variable to check if the function has already being used in your desired context.
And yes, this is not exactly making functions and methods “private”. But part of the point of private and protected methods is having more control over how much of your processes can be accessed from different contexts, or by other developers. The problems is that, by default, you can’t protect a method when you’re forced to call it dynamically – they have to be always public to work that way. Also, you can’t do that with functions at all, because they are always public. But this way you can at least take more measures for your code to be executed exactly when and where you want it to.
Of course there may be other ways to set limits, and even better code examples, but as always, the important thing here is not the code itself, but the problem and the kind of solutions behind it.
Wow! I had no idea those functions even existed. Thanks for sharing. I think they will be quite useful for me in future.
You’re welcome. I’m glad to help 🙂