Justin Tadlock

A better way for plugins to hook into theme templates

An interesting discussion has popped up on WordPress Trac about making it easier for plugins to insert content before/after the post is displayed.

Currently, plugins add features to the the_content hook, which is problematic. Some of these things are sharing icons, related posts, author bio boxes, and much more. The problem is this tends to get rather ugly, especially with some theme designs. Most of the time, the things that plugins add are shown before the post meta or pagination links when using multi-paged posts.

This issue has been around for years. I’m happy that there’s at least a discussion happening about ways to overcome it. If you’re a theme developer, I encourage you jump in if you have a fresh idea.

I wanted to take this discussion to another level though by fully explaining and further refining my comment on the discussion and why it will work for this situation as well as similar situations in the future. Since some of this isn’t directly related to the ticket, I wanted to kick start a discussion on the overall problem.

The problem

Right now, plugins only have a few places where they can reliably hook content into themes. Sometimes, the hooks that are available aren’t ideal for the scenario. The problem is that WordPress can’t simply introduce a ton of new hooks for themes to start using because it takes a long time for standards to form.

Themes are also vastly different from one another. Therefore, it’s hard to standardize some things while also allowing complete flexibility. Some things, such as displaying a post, all themes must do, so there are some areas that are easier to standardize upon than others.

A solution for themes

Assuming something is decided upon and hooks are added before/after a post is displayed, what happens when we run into this problem later with another area? My solution seeks to handle this too.

The idea is that WordPress would have some predefined “template hooks” that themes are strongly recommended to use, at least until they eventually become a standard. Themes would need to register support for this feature and choose the hooks their templates support.

Suppose the first two hooks are the aforementioned before/after post hooks. Themes would simply add this to their functions.php to register support for these hooks:

add_theme_support( 'template-hooks', array( 'before_post', 'after_post' ) );

In the theme templates, such as index.php, the theme would simply have something like the following.

<?php do_action( 'before_post' ); ?>

Post stuff would go here.

<?php do_action( 'after_post' ); ?>

This method would solve the immediate problem with before/after post hooks to some degree as well as provide a method if we need additional template hooks in the future.

If you don’t like the idea of theme authors dropping do_action() into their theme templates, accompanying template tags such as before_post() and after_post() can be easily arranged.

A solution for plugins

If themes were using this proposed method, plugins would be able to easily check if a theme supported a certain hook and fall back to something else if not.

Sticking with the before/after post hooks, a plugin would need something similar to the following code to add content to the post area.

if ( current_theme_supports( 'template-hooks' ) )
    $hooks = get_theme_support( 'template-hooks' );

if ( isset( $hooks ) && in_array( 'after_post', $hooks[0] ) )
    add_action( 'after_post', 'example_plugin_function' );
else
    add_filter( 'the_content', 'example_plugin_function' );

Is this an ideal solution?

Probably not. There’s not perfect solution for this situation. It’s something that will need to be widely adopted and might take a while to work. Whatever solution is decided on will leave plugin authors doing if/else checks in their code.

Another benefit (or problem) with my solution is that we don’t need to change code in WordPress core for it to work. Themes and plugins can actually take advantage of this now.

There are tons of ideas right now. Do you like my proposed method? Or, do you have a better solution?