The hook system that’s built into WordPress is great and really powerful once you fully understand now only how the default actions and filters work, but how you can leverage them in your own themes and plugins to have others work for you.

But there’s a problem that comes with this: Other developers can often abuse them. Perhaps they will name a hook like one that already exists, or perhaps they’ll trigger a hook outside of the normal WordPress lifecycle.

When you’re working on building a plugin that’s adhering to the best practices of using a predefined hook and another plugin ends up breaking the usual flow of control, it can be extremely frustrating.

You – or at least I – can literally spend hours trying to isolate and trace down the source of the problem.

Frustrating, right?

Anyway, I’m not in the business of “calling other people out” or identifying problematic plugins on this site (though I don’t mind to discussing one on one), so this post is not about a plugin that’s doing things in a way that I don’t recommend.

Instead, it’s about finding ways to find a solution when you’re faced with a similar problem.

The WordPress Hooks Fire Twice (or More!)

So imagine this: You’re working on a plugin that’s going to add a new button to the TinyMCE editor in WordPress – maybe you’re doing to be adding one to the toolbar near the existing “kitchen sink” or maybe you’re going to be adding a button above the editor next to the “Add Media” button.

In both cases, WordPress hooks exist for doing just that:

Let’s say, for purposes of this example, that we’re going to be using mce_buttons to add a button next to the “Add Media” button just above the post editor. When running the plugin in isolation, all works fine.

mce_buttons

Great – no big deal, right?

But then you end up deploying the code to Staging, and suddenly, your plugin looks like it’s there – because the button appears – but when you click it, maybe nothing happens. Or maybe multiple things happen all at once.

When this happens, there’s some different things that could be the problem, but one of the best things that you can do is begin to deactivate plugins and then start re-activating them one-by-one until you find the one that’s causing the problem.

In a case like the one that’s being outlined in this post, you’re likely going to find that there’s a plugin that’s triggering a hook – such as mce_buttons – outside of normal WordPress page lifecycle.

And while this may work great for that plugin, it’s also going to completely mess with anything else that’s using that hook because it’s going to fire every single event handler that’s registered with it meaning that your plugin is probably going to be triggered more than once and/or out of order, and then it’s not going to work correctly at all.

If you’re a developer, do not hijack WordPress’ default hooks. Name your own and then use `do_action` to fire them when needed.

Anyway, so how do you go about fixing this? Depending on your implementation and the offending plugin, you’re likely going to find that your solution is going to vary. But if you’re working with the hook that we’re discussing above, then this is how I’ve opted to solve it.

Solving The Problem

Note: I’m not sure if this is the correct way to handle, so if it’s not, please leave a comment outlining a better way to go about doing this.

The plugin that I’m building works in the following way:

  1. Display a new button in the editor above the normal toolbar next to the “Add Media” button
  2. When the button is clicked, display a modal (that accepts input from the user)
  3. Take the input from the modal and initiate an Ajax request
  4. Handle the response from the Ajax request and then enter a result into the post editor

The problem is that if another plugin is hooked into the mce_buttons action, then the plugin will be called more than once and the second (or third, fourth, etc.) instance of the dialog is what the user is going to see.

And the problem is that all of the event handlers defined by JavaScript are registered with the first instance which is hidden behind the other instances.

Still with me?

So to make sure that only a single instance of the dialog is displayed, I introduce a counter variable into the JavaScript, and I increment it when the dialog is displayed.

Next, if the event is called again – by an offending plugin – but the counter has already been incremented, then my plugin doesn’t execute any more.

See the following code:

Again, this works will within the context of this plugin, though I don’t know if it’s a correct way of doing so (and I’m interested in your feedback).

The biggest takeaway from this, in my opinion, is that we need to be careful what we name our custom actions, and we need to make sure that we’re not carelessly triggering other actions that are already built into WordPress as it can introduce a significant level of side effects.

This is frustrating for other developers and users, and it makes your plugin or project look careless – it monopolizes the WordPress experience.

A Note About Development and Staging

One thing that I didn’t mention in the actual article itself, but that’s helpful nonetheless, is that your Development environment should mirror Staging and vice versa.

This means that if there are things on Staging that are not on your local machine, then you need to make sure to pull them down and set them up so that you have a 1:1 configuration. This will allow you to catch problems before they hit staging and save some development time.

Sure, it’s time-consuming on the front-end of the project, but it saves a lot of work towards the middle of the project when you’re cycling through the various test cases. So in addition to everything that’s outlined above, make sure that you have this setup locally and on Staging, as well.

Share:

Leave a Reply