Skip to content

Passing Arguments into WordPress Actions & Filters

WordPress hooks (actions & filters) are by far the most powerful feature within WordPress. They allow for extension of WordPress without the need to change core code. We use them all the time, whether it’s a simple tweak to the WordPress excerpt length, implementing new features into WooCommerce, or adding our own for other developers to hook into the plugins we write. There’s no denying that they’re great, but there’s one key problem. You can’t pass in your own arguments.

Take the following code snippet as an example. Here we’re setting the excerpt_length filter to a value of 10.

function indigotree_excerpt_length() {
    return 10;
}
add_filter('excerpt_length', 'indigotree_excerpt_length');

There is nothing wrong with the above example. It works, and is how the WordPress codex suggests you do it. But…

This approach requires more code, it’s not immediately clear what you’re returning, and you’re never going to use the indigotree_excerpt_length function again in your code base, and it wouldn’t make sense to use it for anything other than the excerpt length.

The WordPress Inspired Way

So, how can this be improved upon?… Well, WordPress has a few functions that simplify returning common values which do exactly this. Unfortunately, they don’t help solve our particular example. I’ve included them below anyway, because they’re useful with other filters.

function __return_false() { return false; }
function __return_true() { return true; }
function __return_zero() { return 0; }
function __return_empty_array() { return []; }
function __return_null() { return null; }
function __return_empty_string() { return ''; }

Suppose we wanted to follow this approach, and have __return_10. We could create a function, just as we did originally, but name it in such a way it can be used anywhere.

function __return_10() {
    return 10;
}
add_filter('excerpt_length', '__return_10');

The obvious advantage here is that it’s no longer tied to the the excerpt_length filter by function name, so this can be reused anywhere you need to return 10 to a filter. Another benefit is knowing exactly how long an excerpt is just by looking at the add_filter call.

You cannot figure out the length of the excerpt from this line:

add_filter('excerpt_length', 'indigotree_excerpt_length');

But, you easily see from this line, the length is 10:

add_filter('excerpt_length', '__return_10');

This is better, but you can’t create a function for every possible value… I know you’re thinking about it.

The Magic Method Way

So, how can we create a function with dynamic names, that can return any value we could ever need?… PHP Magic Methods is how! With a few lines of PHP and some magic method goodness, we can start to call filters with dynamic names, which are quick to write, and informative to read.

add_filter('excerpt_length', 'Returns::int__10');
add_filter('excerpt_length', 'Returns::int__45');
add_filter('excerpt_length', 'Returns::int__101');

To achieve the above, we need a class with the __callStatic magic method. This allows us to handle calls to non-existent methods with the ability to return back a value. I’ve included a simple class below that I’ve written to power the above examples.

class Returns
{
    public static function __callStatic($method, $params = [])
    {
        $instance = new static;

        $parts = explode('__', $method, 2);
        $m = sprintf('return%s', ucwords($parts[0]));

        if (method_exists($instance, $m)) {
            return $instance->{$m}($method, $params);
        }

        throw new \Exception("Method [$method] cannot be found.");
    }

    protected function returnInt($method, $params)
    {
        return (int)mb_substr($method, strlen('int__'));
    }

    protected function returnStr($method, $params)
    {
        return (string)mb_substr($method, strlen('str__'));
    }
}

This works by intercepting calls to functions that start with int__ or str__, removing the first 5 characters, and returning back the rest of the function name as the value, this gives us back 10 when trying to use the method int__10. Perfect.

The best part, this can still be unhooked with remove_filter, so you can use it in plugins & themes and not have to wrorry about annoying future developers that are trying to use your code 😉

The Reusable Function Way

For fun, here’s another solution, it’s simpler, cleaner, and just plain awesome. But please only use this if you’re building stuff for yourself as you cannot unhook it with remove_filter. Absolutely do not use this if you’re building plugins & themes to sell, or to distribute to site owners.

function returns($value) {
    return function () use ($value) {
        return $value;
    }
}

Usage is simple, anything you pass into the returns() function, will be returned wrapped with an anonymous function. It’s as simple as that.

add_filter('excerpt_length', returns(10));
add_filter('excerpt_length', returns(75));
add_filter('excerpt_more', returns('...'));

Summary

There are a number of ways to use filters. Many more than I have touched on, and each have their own upsides and downsides. Choose the best solution for you & your team, but make sure your chosen solution can still be unhooked if need be.

Comments

There are currently no comments on this article. Why don't you add the first?

Have Your Say

Your email address will not be published. Required fields are marked *