Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

How do I revise a theme so that I can publish my event hooks and anyone can build a plugin to add new functionality easily to my theme?

share|improve this question
add comment

5 Answers

After working on several projects so big I didn’t even know all the people involved, I came to one conclusion:

Keep it simple, write great documentation.

Code is simple if it is easy to read, to learn and to extend.

Do not reinvent the wheel: Use the given hooks wherever possible, add new ones in a predictable scheme.

A very basic example:

if ( ! is_singular() && is_active_sidebar( 't5-archive-sidebar' ) )
{
    do_action( 'sidebar_before' );
    print '<ul id="sidebar">';
    dynamic_sidebar( 't5-archive-sidebar' );
    print '</ul>';
    do_action( 'sidebar_after' );
}

By looking at the id attribute anyone can predict the hooks, because the are named always the same. You know already how the hooks for <div id="header"> and <div id="content"> will be named. There is an interesting Trac ticket you should read and the Theme Hook Alliance @Otto recommended in his answer.

Register all callbacks for your hooks in one place: the start of the functions.php. If everything is bound to a hook you don’t need function_exists(), because a plugin or a child theme can just unregister your function and use its own instead.

Example:

add_action( 'content_before',       't5_frontpage_widget' );
add_action( 'footer_before',        't5_loop_navigation' );
add_action( 'header_before',        't5_skiplink', 1, 0 );
add_filter( 'the_title',            't5_fill_empty_title', 20, 1 );
add_action( 'wp_loaded',            't5_post_format_support' );
add_action( 'wp_loaded',            't5_load_theme_language' );
add_action( 'wp_loaded',            't5_setup_custom_background' );
add_action( 'wp_loaded',            't5_setup_custom_header' );
add_action( 'wp_loaded',            't5_setup_sidebars' );
add_filter( 'wp_title',             't5_wp_title_filter', 20, 2 );

Include additional files as late as possible, make it easy to replace those files, and use one file per class.

Use PHPDoc for all functions and classes, add the hook each one is called.

Example:

/**
 * Handles posts without a title. Uses the first 35 caharacters instead.
 *
 * @wp-hook the_title 20
 * @param  string $title
 * @return string
 */
function t5_fill_empty_title( $title )
{
}

The @wp-hook the_title 20 tells the reader exactly when that function will be called and how to remove it. For complex code provide usage examples in the DocBlock.

Avoid code that makes plugin code hard to write:

  • Never include files, declare functions or create global variables in view files (templates). Child theme authors would have to recreate those again – waste of time.
  • Never just run code when the functions.php is called. Bind everything to a hook to give plugins a chance to disable the code.
  • Never use a priority 0.
  • Never use require, require_once or include and include_once in your theme. Use locate_template() instead. In some cases a plugin might register its own directory as an additional theme directory for a child theme. locate_template() allows such a plugin to replace a complete file.
  • Never create anonymous objects.
  • Never put custom post types, custom taxonomies, shortcodes or contact forms in a theme. That is plain plugin territory.

And last but not least: Use version control (Git, Mercurial), write atomic commits, and explain in each commit message why you made this change.

share|improve this answer
    
+1 "Never create anonymous objects" ... and avoid lambda functions as well. –  kaiser Oct 29 '12 at 12:59
add comment

Try to use standardized hooks. Check out the ones from the Theme Hook Alliance: https://github.com/zamoose/themehookalliance

share|improve this answer
    
Interesting, but doesn't take into account the admin panels for a given theme. –  Volomike Oct 30 '12 at 3:18
    
Adding hooks to admin panels is naturally going to be more or less completely custom, because admin panels are generally completely custom. If you're using the Settings API, then other plugins can add to your admin panels easily already, no extra hooks needed. –  Otto Oct 30 '12 at 14:06
add comment

There really is no definite answer to your question: How to make a theme "plugin-ready"?

Although, there are several things which you should make use of in your theme. I cannot list every single thing you should do in such great detail. However, I can provide a short list with a quick explanation why you should be using them.


WordPress Core Functions

  1. add_theme_support(); - Adding theme support allows plugin developers to check if certain theme functionality is present, and inject additional functionality to each.
  2. register_sidebar(); or register_sidebars(); - Having dynamically created sidebars, allows plugin developers to manipulate the sidebar output by either hiding what's already there, adding more content or removing a sidebar entirely.
  3. register_nav_menu(); or register_nav_menus(); - Allows plugins to highly manipulate your navigational structure, like aditing a menu item, or adding a new menu item, or altering CSS styles, etc... Adding permission settings to navigations entirely, or specifically to particular navigation menu items.
  4. wp_register_sidebar_widget(); and wp_set_sidebars_widgets(); - Widgets really go well with your dynamic sidebars from bullet number two. This again, allows extreme high flexibility with your theme. Allowing developers to add custom widgets, and inject them into your dynamic sidebars for further functionality or content output.
  5. get_header(); get_footer(); get_sidebar(); or get_template_part(); - Using WordPress built-in functions for obtaining sections of your theme, allows for plugin developers to again, manipulate your theme output, by either inserting additional code to your theme, removing code from your theme, or changing the effect of your theme, by using hooks, which I will cover next, after this list.
  6. wp_head(); wp_footer(); wp_title(); and body_class(); - These functions are excellent for plugin developers. This allows plugin developers to enqueue new scripts or styles or dequeue existing scripts or styles from your theme's header and footer. Using wp_title(); allows plugin developers to manipulate your theme's title tag output. Great for SEO plugins. body_class(); can really be used for quite a bit. I really highly recommend you build support for this in all of your themes.

If you are ever unsure of a particular function, and you know it has to exist somewhere, try visiting Query Posts, for a better WordPress code reference.


WordPress Hooks (Actions and Filters)

The next thing you should consider would be WordPress Hooks, or... Actions and Filters to be politically correct.

By default, WordPress already provides support for many events triggered in your theme if you're using those functions posted in the numbered list, above this section.

These "events" are referred to as Hooks, which allow plugin developers to add, modify, or remove code from certain areas within your theme. Or fire a certain event, when another event is triggered in your theme.

Which brings us to the next area you should be thinking about, while creating a "Plugin-Ready" WordPress theme.


Creating your own WordPress Hooks

It would be wise to learn how to create your own WordPress actions and WordPress filters, within your theme. This will allow plugin developers to HIGHLY manipulate your WordPress theme.

If you don't know how to create WordPress actions or WordPress filters, click here to learn more about creating WordPress actions with do_action(); and click here to learn more about creating WordPress filters with apply_filters();.

Simply just by using more of the default functions provided by WordPress core (Like the ones listed in the first numbered list at the beginning of this answer, or else here for a full function index), a LOT of the hooks that developers would need to use, are already defined by the core.

Do not ever be shy with creating your own WordPress hooks, within your WordPress theme. It's always better to have many many hooks available for developers to tap into and make use of, rather than not enough to do what they need to do.

Just remember to use unique references to your hook names. (They must be unique, so that they do not conflict with either existing WordPress Core hooks, or existing WordPress hooks created by other plugin developers.)

By creating your own hooks, WordPress plugin developers can hook into your custom created WordPress hooks using add_action(); and add_filter(); and configure the output or behavior of your WordPress theme, without actually making changes to your theme's core code.

Which is great, when you release an update for your theme, because their changes to your theme will be persistent and won't be over-written or lost by the theme update.

As per @Otto's answer, you can try using standardized hooks, provided by the Theme Hook Alliance.


share|improve this answer
1  
Some nice bits to be had there! I'd add Adam Brown's hook database & Rarst's Query Post to your recommendation for an absolute/comprehensive list of functions/hooks. The WordPress Codex, while good, is largely incomplete and can not be relied upon solely. –  userabuser Oct 29 '12 at 14:31
add comment

As an alternative to your answer why don't you do something similar to this instead?

In your theme file place a hook,

Step 1: single.php

<div class="post">

    <?php the_title(); ?>

    <?php the_content(); ?>

    <!-- your hook -->
    <?php after_post_content(); ?>

</div>

Step 2: functions.php

In your functions.php we want to let WordPress know about this hook...

function after_post_content(){

    do_action('after_post_content');

}

Step 3: In your plugin

As the name suggests, hooking onto after_post_content allows us to inject our own logic/markup right after the_content as shown in single.pgp. In this case, we are just echoing some basic phrases, however you might use it for injecting social media buttons, special adverts, e-mail subscription boxes or any other creative idea you can come up with...

add_action('after_post_content', 'my_callback_function', 10);

function my_callback_function(){

   if ( is_single('special-page-slug') ) {
       echo 'how you doin?';
   } else {
       echo 'yo!';
   }

}

Overall, the above ↑ is only a simplified example of how you can create hookable actions/filters for your themes but provides plugin or even theme developers/users readily accessible means to inject or filter content. Its a more efficient process and easier to manage.

This is just the start of what's possible...

share|improve this answer
    
My strategy was to make it kind of brainless and consistent so that one doesn't have to keep thinking up custom events all over the place. Note that my strategy provides a consistent way to handle POST, GET, reaction to $_SERVER variables and get_option() settings strings, and also a consistent way to handle pre and post content display. Now, granted, once this is done, however, if one wants to take it to the next level and add even more custom events as you suggest above, then yes, that's probably going to help, where warranted. –  Volomike Oct 28 '12 at 19:54
    
@Volomike What are the inconsistencies you think exist with handling $_POST/$_GET variables etc in WP? Because I don't have a problem working with these responses, especially where existing WP hooks are concerned. Genuinely want to know your thought process on this. Although, I don't recommend your method, if you really had to do it your way you'd be better of using a wrapper function that you can use at the start and end of your template file that output your code which is too verbose to be scattered across template files in its current form. –  userabuser Oct 29 '12 at 13:49
add comment

1) Separate every theme file so that you have PHP at the top, and HTML at the bottom. Then, in the HTML portion, inject variables in like so:

<?= $view->MY_VAR ?>

or use PHP Alternative Syntax stuff minimally for loops and if/then/else conditions. Keyword -- minimally. See, the bulk of your PHP should be at the top of the file.

You might be scratching your head on why the $view->MY_VAR. Okay, here's why:

$view = (object) array()
$view->MY_VAR1 = '<strong>Test1</strong>';
$view->MY_VAR2 = '<em>Test2</em>';
print_r($view);

See how easy it is to debug? Plus, because I use ALL CAPS on the variable name, I can find them easy in the HTML portion of the page. And why did I call it $view? Because it's shorter than typing $template, and because people who understand this style of variable injection usually know something called MVC (a computer science term) where V stands for View.

2) Move any library or class file includes to the top of the PHP file such as:

require_once('mailer.php');
require_once('gallery.static.class.php');

3) Following the library includes, set any constants or static pieces of information, or read from get_option() API.

4) Following the "settings" initializing area (step 3), do any $_GET, $_SERVER, or $_POST settings where you read those things and stick them into variables, or may need to do things on them like strip_tags(), stripslashes(), trim(), etc. Basically you should be doing any input processing, not doing any real hardcore programming yet.

5) Following the GET/POST variable processing (step 4), add this snippet of code, and note that I used "mytheme_" as a prefix for the theme name, but you can change "mytheme_" to something else.

// expose our WordPress plugin event hook - pre, meaning after GET/POST processing but before actually doing anything else
$sHook = strtolower(basename(__FILE__));
$sHook = str_replace('.php','',$sHook);
$sHook = str_replace('-','_',$sHook);
$sHook = str_replace('.','_',$sHook);
$sHook = preg_replace('/[^a-z0-9\_]/','',$sHook);
@ do_action('mytheme_pre_' . $sHook,get_defined_vars());

6) Right before you do the ?> before you start doing the HTML portion of the file, add this snippet of code:

// expose our plugin post event hook - post, meaning right before showing content
@ do_action('mytheme_post_' . $sHook,get_defined_vars());
// turn on output buffering
@ ob_start();

7) At the end of the file, after all the HTML, add this snippet of code:

<?php
try {
  $sContent = ob_get_contents();
  ob_end_clean();
  $sContent = apply_filters('mytheme_' . $sHook,$sContent,get_defined_vars());
  echo $sContent;
} catch (Exception $e) {
  echo "\n<!-- ob_* function error -->\n";
}
// no ending ?> is required here

//////////////

Now that this is done, you can code a fresh plugin and when you use add_filter() or add_action(), your callback function will be given all the defined variables of the page (and the content as well if this is add_filter()), and you'll be able to pretty much override anything in the theme.

You can use echo "<h2>$sHook</h2>\n"; on your pages to document what all your event hooks will be.

Don't forget to do this even on the admin panels you create with your theme, not just the frontend of the theme.

Note on the several lines above you can reduce this a little with a couple functions, such as one for finding the hook name. Just don't put the line that has do_action(), apply_filters(), or get_defined_vars() inside a function or things will not act as you expect them to do.

share|improve this answer
1  
I'm scratching my head alright. You ask a question and answer it within seconds with what appears to be a prepared answer of your own. That makes this question, a tutorial, not a real question. This doesn't seem like a practical way to make your theme "hook" ready. Instead, why don't you define hook locations in your theme files? Then in your theme functions.php file you create the function that relates to the hook and use a call to do_action within this function. Then you (plugins or theme users) can do the traditional add_action('my_hook', 'my_callback');. Same applies for filters. –  userabuser Oct 28 '12 at 9:13
    
@userabuser Answering your own question is very welcome. –  toscho Oct 28 '12 at 14:40
1  
Sounds like @userabuser's discussion needs to be taken up with meta. –  Volomike Oct 28 '12 at 19:46
1  
@userabuser: +1 for the hook location. –  Sisir Oct 29 '12 at 11:38
1  
Please have a look at the recommended codex.wordpress.org/WordPress_Coding_Standards –  Wyck Oct 29 '12 at 14:38
show 6 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.