Rethinking template tags in plugins

Some plugins offer their own template tags that the blogger can add to their theme where they’d like to display it. Some may even be loaded with an array of arguments to further customize the options. We’ll stick to a simple myplugin_related_posts() template tag as an example throughout.

Usually, these plugins can’t or aren’t willing to take advantage of hooks, such as appending information to the post content, or widgets. That’s okay, especially so when they also offer those alternatives, but also include a theme template tag.

I want to rethink this process. Some bloggers are weary of modifying templates, some plugins’ instructions are plain dangerous, and some bloggers would love to take advantage of a plugin, but it doesn’t allow much flexibility with its output.

Take this as an example:

// Add this to your theme
myplugin_related_posts();

But, what happens when myplugin is deactivated? That function suddenly no longer exists, and the blog blows up. Of course, most plugin authors realize this, and encourage this instead:

// Add this to your theme
if ( function_exists( 'myplugin_related_posts' ) )
    myplugin_related_posts();

Not bad, but clunky. How can we do this better?

Using Hooks for Template Tags

Dion Hulse blew my mind away when he mentioned this method to me a few months ago. Sure, all developers use hooks on a regular basis, but the ingenuity of using a hook as a template tag was too good to resist.

As an example:

// Add this to your theme
do_action( 'myplugin_related_posts' );

Wow, how cool is that? Look ma, clean code! And no fatal errors! Now all the plugin author needs to do is add their existing template tag to that action:

add_action( 'myplugin_related_posts', 'myplugin_related_posts' );

And, if the plugin author doesn’t provide that, then you can do it yourself in your theme’s functions.php, right next to perhaps other filters and actions that modify or add to the hundreds of template tags built into WordPress.

What’s to stop you from adding arguments?

// Add this to your theme
do_action( 'myplugin_related_posts', array( 'count' => 5 ) );

Or, what’s to stop you from removing, replacing, or adding additional callbacks to the same hook?

If the template tag returns a result and needs to be echoed, you can attach the tag to a filter instead:

echo apply_filters( 'myplugin_get_related_posts', '' );

There are a handful of reasons why I really like this method:

One, it requires no effort for the plugin author to passively support this, as most template tags can already be used as callbacks. To actively support this, they can include an add_action call in their plugin, for the times the hook exists.

They don’t need to require the use of a hook, of course — the widget, template tag, hook, etc., all can be supplied, and the user can choose what to use.

Two, administrators can do this themselves by tying the template tag to a hook on their own. They may need their own wrapper function to support more complex tags, but it’s generally worth it.

Three, if the hook doesn’t exist in the theme, then the plugin’s add_action doesn’t do anything. The converse is that if the callback doesn’t exist, then the action hook doesn’t do anything. No fatal errors.

Four, it encourages plugins and themes to play nice with each other through clean, flexible, and robust methods.

Of course, the template-tag-as-a-callback method is only for those situations where the tag needs to be placed in a particular spot. There’s nothing stopping you from taking the template tag provided by the author and simply leveraging the existing hooks system to append the HTML to the post using the the_content filter.

Leverage the WordPress plugin API and reap the benefits.

28 thoughts on “Rethinking template tags in plugins

  1. That definitely should become the standard way plugin documentation should explain to new users how to use the plugin’s functions.

    I wish there was something as clean for shortcodes. The classic problem being a plugin offers a shortcode. The shortcode is used in a bunch of posts. Plugin is deactivated. Posts now have weird bracketed text in the content.

    • I’m hoping this idea gains traction. Theme developers, plugin developers, and users should all like this approach for its ease and simplicity.

      If you’re using a shortcode in posts directly and want to deactivate it without removing all references to it, I’ve sometimes re-registered the shortcode in functions.php to return nothing. I agree it’s not ideal, and it’d be nice if there was a cleaner way to do it. Problem is, we would have to keep track of any shortcode ever registered on the blog, otherwise [garbage] would be considered a shortcode when it is not and never was.

  2. I’m usually customizing templates to call my special functions in funcitons.php. So maybe that’s why I’ve not been bitten by the deactivation time-bomb. But, I’m beginning to write plugins now, so I think the lesson you teach here will be very helpful for me.

    @Chris Coyer, the one plugin I have written commits the exact shortcode problem you bring up. oops!

  3. I am not certain I understand the advantages of this approach, Andrew.

    I mean, why having hooks whose names are identical to plugin template tags, and not just try to standardize a few new action hooks whose names would suggest what they actually are and when they are actually launched?

    E.g., wp_content_before and wp_content_after.

    I was hoping, and had suggested, that we could start something like that with Twenty Ten, but now it’s too late for v3.0 probably.

    PS. I hate the box-shadow that Twenty Ten uses in the textareas. It makes them look dirty.

    • The advantages of this approach are rooted in the function_exists check (or lack thereof) and also allowing your plugin to play nice with other plugins and in a professionally run installation.

      If a theme wants to provide custom hooks, then so be it — but something tells me they’d be used much more by child themes and such (and that was the point of the sole action hook in Twenty Ten, twentyten_credits). Adding such hooks in plugins aren’t very flexible for plugins as they may not exist. Indeed, we have that trouble with the head and footer hooks being inconsistently implemented.

      Those hooks are also specifically for elements that belong in , and then scripts that should be referred — neither of those have to do with direct content, nor should we make assumptions that all hooks apply to all themes. So I’m a huge -1 to new action hooks that themes need to add.

      P.S. Yeah, not entirely a fan of the box shadow either.

      • Yeah, maybe I am overoptimistic when I think we could get all themes to standardize on new hooks like wp_content_before and wp_content_after.

        Thank you for the reply and for the additional explanation. I started playing around with the do_action idea to see how it works in practice and if I can wrap my head around it.

  4. Andrew,
    Great writeup about a practice that I don’t see in the wild very often. I agree that this should be the defacto way that plugin development is taught. It’s funny, I was actually criticized a few months back for implementing this in one of plugins. I just replied “I read somewhere that this was the best practice”. I’m adding this post to my resource list.
    -Mike

  5. K2 offers a number of built in template hooks (as do many other ‘theme frameworks’ and ‘parent themes’) and when developing a project for myself or a client I take this approach, but have only recently started to toy with the idea of doing something like this for the plug-ins I release. At the end of the day it seems like its a bit 6 to one, half dozen to the other type of thing, since the instructions to copy code equate to the same amount of work for somebody implementing the instructions, but less code clutter is a good thing, and since multiple callbacks can be attached to the same hooks, provides infinite more flexibility. I also like the idea of providing filters inside of the template tags so that others can modify it further — all without touching your code.

    You’re on a roll with these posts, Andrew. Keep up the good work.

  6. Oh, great idea. I love the idea of using hook instead of checking function’s existence very much. It provides the flexible way for users to add some functionality to template. I hope this guide will be included in the Codex for all developers to know.

  7. i think this approach can help to push WP Theme more OOP, zend-like, and clean. it’s also help to avoid awful mess when PHP and HTML put together in one place. Some says, hybrid code is the symptom of bad coding practice.

    Maybe, this approach can be introduce more and more in another part of wordpress. For example, in dashboard. I think dashboard has too many hard-coded function and make this section experience lack of flexibility.

  8. Hi Andrew,

    I agree in principle. But I disagree in practice.

    I’ll start by saying I view myself as a hardcore developer and not a designer at all so for me this is not about what I want but what I see to be good for the platform. One of the reasons WordPress has been so successful is that it’s made it relatively easy for designers who never want to “code” be able to design for WordPress while thinking they really were not programming. That’s why the name “template tags” came into existence in the first place; otherwise they would have just been called “functions!”

    Including in a theme doesn’t require as much cognitive dissonance for a designer as having to type If you push that as a best practice I think you might well alienate designers. But still, I see the value.

    In pondering the idea I have a simply proposal. It’s not the absolute best I can come up with but it only requires learning one thing and I think that one thing might not be so hard to handle (it also required PHP5; yet another reason to move forward…)

    Rather than I propose using overloading on global $wp and instead use the_custom_tag; ?> Reserve “the_” and “get_” for things that echo and things that return (note I didn’t include “()”; that was intentional.) These could then do the do_action() you proposed but within the object’s __get() or __call() methods.

    Just imagine how clean if could be to have a loop that looks like this:

    if ($wp->no_posts):
    _e('Sorry, no posts matched your criteria.');
    else:
    echo '';
    while ( $wp->has_posts ):
    echo "{$wp->get_the_content}";
    endwhile;
    echo '';
    endif;

    Of course these are just the starting ideas. But I think if you run with them we can get as close to the best of all worlds as possible while still being syntactically correct in PHP.

    • The post wasn’t targeting theme developers — the template tags they use will always exist, either because they are core WordPress functions, or are included in the theme. The few themes that try to integrate with popular plugins when they are active are written by developers who may very well be already using this trick.

      The target of the post was authors of plugins which provide template tags, requiring the user to edit template files. Of course, the plugin might also offer a widget, maybe attach to a content hook, etc., but the plugin will generally also be packaged with a function that can print into a template.

      I wonder if I didn’t emphasize enough (I bolded the line shortly after this post was published) that this was designed not only for incredible flexibility, something Eric Marden expressed in the comments above, but also for compatibility. When your plugin is deactivated, that template tag either has to be removed, or wrapped in a function_exists call, or you cause a fatal error.

      Using a hook here allows for infinite flexibility in that regard as any other. It also allows sites/networks run by serious developers and administrators to integrate your plugin and customize it to exactly their need.

      Thanks for stopping by with your insights — always appreciated. I personally find the current loop quite powerful and simple, and it is getting stronger with each version — in 3.0′s case, get_template_part() — and I do hope to see additional improvements in the future.

      • The post wasn’t targeting theme developers…The target of the post was authors of plugins which provide template tags, requiring the user to edit template files.

        I think I’m missing something then. Who actually modifies the template; plugin dev, themer or end-user? If the latter, aren’t they the ones that need things to be least intimidating?

        … this was designed not only for incredible flexibility, something Eric Marden expressed in the comments above, but also for compatibility. When your plugin is deactivated, that template tag either has to be removed, or wrapped in a function_exists call, or you cause a fatal error.

        Using a hook here allows for infinite flexibility in that regard as any other. It also allows sites/networks run by serious developers and administrators to integrate your plugin and customize it to exactly their need.

        I understand, and completely agree with the value of using the hook. I was instead concerned about how intimidating it looks to non-developers )(if I misunderstood and this isn’t something you expect end-users to add tthen forgive me, my points are moot.)

        Instead I was suggesting we consider an alternate with the same benefits but without the complexity. I assume you are familiar with the __call() and __get() methods in PHP, correct? I didn’t state explicitly but they could be used to wrap a hook call but with a cleaner syntax; that was what I was suggesting.

        …your insights — always appreciated. I personally find the current loop quite powerful and simple, and it is getting stronger with each version — in 3.0′s case, get_template_part() — and I do hope to see additional improvements in the future.

        I still find it less elegant than I would like which is why I was exploring the above syntax.

        On a somewhat related note I’m constantly struggling with the fact that tht get_*() functions are not as feature complete as the the_*() template tags. I like to use PHP Heredoc syntax for composing HTML code and you that that the_*() template tags are not usable; I need fully functionaly get_*() functions instead.

  9. Pingback: Rethinking themes « Rethinking Themes

  10. I think that using hooks is lieu of template tags is an awesome idea in theory, but not that compelling in practice. It’s too inconsistent with the way you normally go about building themes in WP. WP provides template tags, so it’s a conceptual shift for normal users to mix hooks into the mix, especially messing with filters. (It took me a long time to actually get what was going on with filters.)

    The straightforward-ness of template tags is one of the reasons WP has taken off despite the intimidation (for normal people) of using straight PHP in templates rather than a templating language.

    Another weakness here also is this common scenario: if (function_exists('wp_page_navi')) : wp_page_navi(); else : (built-in WP nav tags here) endif;

    Or, the short version: the very people we’re trying to protect with this (relatively unsavvy users) are the very people who probably won’t get what we’re doing. People smart enough to get it don’t need this protection.

    • The if/else is certainly a weakness.

      The people who we’re trying to protect may not really get what they’re adding, only that they’re adding it. Telling them to add do_action('my_plugin_template_tag'); instead of if ( function_exists( 'my_plugin_template_tag' ) ) my_plugin_template_tag(); or worse, my_plugin_template_tag(); doesn’t make much difference to them, except that it will prevent fatal errors (see the latter case) and is more flexible in general.

      Thus, my argument was two-fold: If it is feasible, change your instruction to a do_action call, and regardless of instructions, calling add_action for your template tag makes your plugin more flexible.

      All that said, it’s just a creative idea.

  11. Pingback: My Weekly WordPress-related Tweets, for 05/23/2010 | Valent Mustamin

  12. Pingback: Best On WordPress From The Past Week N.7 » wpCanyon

  13. You’ll be happy to know that I’ve changed my default coding style to make use of do_action and I have to say, its a lot cleaner and easier to roll out. Will be updating my open source plugins that publish template tags to do this as well, but used it on a client site recently.

  14. Pingback: wordpress filters documentation? Trying to understand add_filter() | DEEP in PHP

  15. Pingback: Rethinking template tags in plugins | Daniel Bachhuber's weblog

  16. Hi Nacin, Just wanted to let you know that I’ve recently updated one of my plugins. One of the main goals was to add a solid way of integrating it into themes and other plugins. I choose to explicitly tag all functions as “private” with phpDoc @access tag and then added custom filters for specific functionality that I wanted to make “public”. add_filter() was chosen for all the hooks. I even went one step further and coded each “Template Tag” style function to throw a notice if it is accessed outside of it’s designated filter.

    I really like the idea of using custom hooks as the supported method of interfacing with a plugin and I can only hope that more developers adopt this practice. If you want to check out what I came up with, here’s the docs: wordpress.org and here’s the source: Github.

  17. I’ll bite.

    Using actions is not unlike using keyframes in other constructs. You know at a specific point in time, relative to the time around it, that foo is going to happen. Maybe bar has happened it, maybe not. But it’s time to do foo. That’s how WordPress actions (kinda) work, they’re just safe wrappers.

    I think we’re on the tipping point of this becoming more and more common in WordPress, and there are places where WordPress core could benefit from similar thinking. Turn wp-settings.php into a stream of do_actions() and then let everything hook in the proper order. Viola, it inherently makes everything modular and pluggable. Turn themes into template parts with associated functions to call them into actions, and now everything is modular, and degrades gracefully when something is missing. Turn those functions into shortcodes, and you can even pull in template parts mid-stream.

    Bingo.

    Alas, the benefit to using ‘template tags’ is that functions are universally understood between most popular programming languages; they’re just plain easy to understand. If you’ve ever once programmed a foo, you know “foo1 causes foo2″ as opposed to “foo1 calls foo2 that’s in a queue of foo’s, but you need to call foo3 to add foo1 to foo2. foo2 may have already happened though, so foo1 may never happen either. Since there’s no foo counter, and any other foo might unfoo your foo, your foo’s are futile.”

    Oh, and remember what I said about safe wrappers? Remember what you said about function_exists() checks? It isn’t 100% accurate. Even if you use do_action() by adding a function that doesn’t exist, it white screens and wets the bed the exact same way. Without running a function_exists() on every do_action(), we’re not really gaining anything besides extra complexity and an extra level of white screen prevention.

    Thought proving concept none-the-less, and I for one would endorse more of it. :)

    • Remember what you said about function_exists() checks? It isn’t 100% accurate. Even if you use do_action() by adding a function that doesn’t exist, it white screens and wets the bed the exact same way.

      Looks like you’ve mixed up do_action() and add_action(). do_action() does not take a callback, it takes an action name. So as long as the plugin is doing the add_action(), if the whole plugin disappears, then the theme won’t flinch. The action will be run, and nothing will be attached to it.

      If the plugin doesn’t follow the methods outlined here, and you need to do add_action() on your own in functions.php, then yes, you’ll probably want to do a function_exists() check. But you’d want that anyway if you were just dropping the function into the theme.

      • If I call add_action( ‘foo’, ‘bar’ ); and the function ‘bar’ doesn’t exist, what happens when do_action( ‘foo’ ) runs? Smart guy. :)

Comments are closed.