Have you ever looked at the add_action function in WordPress? Here it is:
function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) { return add_filter($tag, $function_to_add, $priority, $accepted_args); }
I know, right? Some people’s minds just got blown.
What are Filters?
A filter is defined as a function that takes in some kind of input, modifies it, and then returns it. This is an extremely handy little concept that PHP itself uses in a ton of different ways. About half the string functions qualify as a ‘filter’ function.
Look at strrev(). It’s a simple-stupid example. It takes a string as an argument, and then returns the reverse of that string. You could use it as a filter function in WordPress, easily. Like, to reverse all your titles.
add_filter('the_title', 'strrev');
Some filters take more than one argument, but the first argument is always the thing to be modified and returned. PHP adheres to this concept too. Take the substr() function. The first argument is the string, the second and third are the start and optional length values. The returned value is the part of the string you want.
What are Actions?
An action is just a place where you call a function, and you don’t really care what it returns. The function is performing some kind of action just by being called. If you hook a function to the init action, then it runs whenever do_action(‘init’) is called.
Now, some actions have arguments too, but again, there’s still no return value.
So in a sense, a WordPress action is just a filter without the first argument and without a return value.
So why have them both?
Because there is still a conceptual difference between an action and a filter.
Filters filter things. Actions do not. And this is critically important when you’re writing a filter.
A filter function should never, ever, have unexpected side effects.
Take a quick example. Here’s a thread on the WordPress support forums where a person found that using my own SFC plugin in combination with a contact form emailer plugin caused the email from the form to be sent 3-5 times.
Why did it do this? Basically, because the contact form plugin is sending an email inside a filter function.
One of the things SFC does is to build a description meta from the content on the page. It also looks through that content for images and video, in order to build meta information to send to Facebook. In order for this to happen at the right time, the plugin must call the_content filter.
See, what if somebody puts a link to a Flickr picture on their page? In that case, oEmbed will kick in and convert that link into a nice and pretty embedded image. Same for YouTube videos. Or maybe somebody is using a gallery and there’s lots of pictures on the resulting page, but the only thing in the post_content is the gallery shortcode.
In order to get those images from the content, SFC has to do apply_filters(‘the_content’,$post_content). This makes all the other plugins and all the other bits of the system process that $post_content and return the straight HTML. Then it can go and look for images, look for video, even make a pretty 1000 character excerpt to send to Facebook.
But SFC can’t possibly know that doing apply_filters(‘the_content’,…) will cause some other plugin to go and send a freakin’ email. That’s totally unexpected. It’s just trying to filter some content. That would be like calling the strrev() function and having it make a phone call. Totally crazy.
Shortcodes
Shortcodes are a type of filter. They take in content from the shortcode, they return replacement content of some sort. They are filters, by definition. Always, always keep that in mind.
Also keep in mind that shortcodes are supposed to return the replacement content, not just echo it out.
Conclusion
So plugin authors, please, please, I’m begging you, learn this lesson well.
Filters are supposed to filter. Actions are supposed to take action.
When you mix the two up, then you cause pain for the rest of the world trying to interact with your code. My desk is starting to get covered in dents from me repeatedly banging my head into it because of this.