How to hack a WordPress plugin that doesn’t have the filters you want

This post is more than 12 years old.

The best thing about WordPress, besides the fact that nearly anyone can edit a website built with it, is hooks. Filter and action hooks allow developers like me to customise a WordPress website in myriad ways. Many good plugins provide hooks too. But inevitably, you’ll run up against a problem where you’d like a plugin to have a hook that it just doesn’t have. You can ask the plugin author nicely to add that hook, and maybe they’ll add it sometime soon, maybe even on time for your deadline. But what if your deadline comes before they add it?

OK, the easy way around this is to just hack the plugin code and make it do what you want it to do. Sure, easy… until they update the plugin and you have to merge your dirty hack back into their updated version. And maybe it won’t fit any more. Or worse: your client updates the plugin, and then blames you when their website goes offline or loses sales for a month.

Right, so: the easy way will hurt you at some point, so don’t to that! Instead, look for ways to do what you want to get done, using the hooks that WordPress provides you with. Here’s a couple of ways I’ve handled this on some recent websites.

Post Query Hooks

When a plugin uses WordPress post queries to get their data, you can take advantage of WordPress’ various filter hooks to augment those queries to get the results you need. In a recent blog post “Sort wp-e-commerce products by category and product title“, I showed how to use this approach to change the database query that wp-e-commerce uses to build a page, and sort it in a custom way. The crux of that post was to:

  1. use the filter hook ‘parse_query’ to check for conditions that mark a posts query as a wp-e-commerce product query
  2. use the filter hook ‘posts_join’ to add the category name to the SQL query
  3. use the filter hook ‘posts_orderby’ to change the SQL order by clause

I won’t repeat the code here because you can see it on that post, but it bears mentioning as a general approach to the problem of changing how a plugin retrieves a list of posts when it doesn’t give you any filter hooks to change its behaviour: use WordPress hooks.

Option Hooks

Another approach that I’ve used a few times, and again just today, is to intercept the plugin fetching its options and change them before the plugin can use them. Often, a plugin makes use of options to drive how it behaves, and you can change the plugin’s behaviour by changing those options on the fly. It’s a bit kludgey, but needs must!

The WordPress function get_option has some filter hooks that let you intercept any calls to it, both before and after it retrieves the options. In particular, at the end of the function, it calls a filter that lets you change the value of the option, and it’s nice enough to call a filter specific to that option!

return apply_filters('option_' . $option, maybe_unserialize($value));

As an example, here’s what I did to add some extra posts to be excluded by Google Sitemap Generator. That plugin gets all of its options with a call to get an option called “sm_options”:

function filterSitemapOptions($opts) {
    global $wpdb;

    // tell it to exclude any pages that are just page-links-to links to PDF files
    $exclude = $wpdb->get_col("select post_id from $wpdb->postmeta where meta_key = '_links_to' and meta_value like '%.pdf'");
    if (!empty($exclude)) {
        $opts['sm_b_exclude'] = array_merge($opts['sm_b_exclude'], $exclude);
    }

    return $opts;
}

add_filter('option_sm_options', 'filterSitemapOptions');

Localisation Text Filter

[added 2013-06-10; I use this quite a bit, especially with WooCommerce field labels]

If you want to change some text that the plugin is generating, and it’s using the Localisation functions in WordPress to show different translations of that text, then you can intercept calls to localise the text and substitute with your own text.

For example, WooCommerce uses the term “Sort Code” for the bank branch code when the customer is paying by direct deposit; in Australia, we call that a “BSB”. I customise WooCommerce for Australian websites by hooking the gettext filter and checking the text domain and pre-translation text before replacing with my text.

add_filter('gettext', 'filterWooGetText', 10, 3);

/**
* change some WooCommerce labels
* @param string $translation
* @param string $text
* @param string $domain
* @return string
*/
function filterWooGetText($translation, $text, $domain) {
    if ($domain == 'woocommerce') {
        if ($text == 'Sort Code') {
            $translation = 'BSB';
        }
    }

    return $translation;
}

Simple, clean, and will get the job done without being undone when the plugin updates.