WHAT'S IN THIS CHAPTER?
Hooks are the backbone of WordPress. They enable plugin developers to "hook" into the WordPress workflow to change how it works without directly modifying the core code. This enables users to easily upgrade to newer versions of WordPress without losing modifications.
If a developer modified the core code, those edits would be lost the next time WordPress was updated. The update would overwrite all those changes. Using hooks enables you to develop plugins in separate folders apart from the core, keeping the plugin code safe from updates.
Without hooks, plugins would have no way to modify how WordPress works. The hooks system you learn about in this chapter is used throughout the book and is something you will use in nearly every plugin you create. After you learn how to use hooks, you will understand exactly why WordPress is such a powerful platform and has thousands of plugins built for its millions of users.
WordPress has two primary types of hooks: action hooks and filter hooks. The former enables you to execute a function at a certain point, and the latter enables you to manipulate the output passed through the hook.
Hooks aren't just for plugins. WordPress uses hooks internally. If you browse through the core source code, you can see many examples of how WordPress uses its own system to hook into itself.
Action hooks enable you to fire a function at specific points in the WordPress loading process or when an event occurs. For example, you might want a function to execute when WordPress first loads a page or when a blog post is saved.
You need to understand the do_action() function. When hooking into WordPress, your plugin won't call this function directly; however, your plugin will almost always use it indirectly.
<?php do_action( $tag, $arg = '' ); ?>
$arg — Value(s) passed to registered actions. It looks like a single parameter, but this isn't always the case. Action hooks have the option to pass any number of parameters or no parameters at all. You need to check the WordPress source code for specific hooks because the number of parameters changes on a per-hook basis.
Following is an example of what an action hook would look like with multiple parameters.
<?php do_action( $tag, $arg_1, $arg_2, $arg_3 ); ?>
Now take a look at a WordPress action hook called wp_head and how it appears in WordPress. This hook is fired within the <head> area on the front end of the site. WordPress and plugins usually use this hook to add meta information, style sheets, and scripts.
<?php do_action( 'wp_head' ); ?>
When this code fires in WordPress, it looks for any actions registered for the wp_head action hook. It then executes them in the order specified. As you can see, it has a name of wp_head but passes no extra parameters. This is often the case with action hooks.
Following is an example of an action hook that has two extra parameters.
<?php
do_action('save_post', $post_ID, $post);
?>Here, you can see that the hook name is save_post and the parameters it passes are $post_ID and $post.
An action is technically a PHP function. For a function to be considered an action, it would need to be registered for an action hook. In the previous section, you can see what action hooks are, but for action hooks to serve any purpose, they need to have an action registered for them.
That's where plugins come in. You develop custom functions (actions) that perform a specific task when the action hook is fired. To do this, you would use the add_action() function.
<?php add_action( $tag, $function, $priority, $accepted_args ); ?>
$tag — The name of the action hook your function executes on.
$function — The name of your function that WordPress calls.
$priority — An integer that represents the order in which the action is fired. When no value is given, it defaults to 10. The lower the number, the earlier the function will be called. The higher the number, the later it will be called.
$accepted_args — The number of parameters the action hook will pass to your function. By default, it passes only one parameter.
Action hooks aren't limited to a single action. Your plugin can add multiple functions to an action hook. Other plugins, and even WordPress core, often add functions to the same hook.
Now it's time for you to put action hooks to use. One common action hook is wp_footer. It is fired on the front end of the site by the user's WordPress theme. Generally, it is fired just before the closing </body> tag in the HTML. In this example, you're going to register an action for the wp_footer hook that adds a custom message to the footer.
<?php
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
function boj_example_footer_message() {
echo 'This site is built using <a href="http://wordpress.org"
title="WordPress publishing platform">WordPress</a>.';
}
?>Code snippet boj-example-footer-message.php
Take a closer look at how you used add_action() from the preceding code.
<?php add_action( 'wp_footer', 'boj_example_footer_message', 100 ); ?>
The first parameter is the name of the hook (wp_footer). The second parameter is a callback to your custom function (boj_example_footer_message). The third parameter is the priority (100). Your function will likely be executed much later than other functions hooked to wp_footer because of its priority of 100. If this number were changed to 1, it would be called earlier.
It should be noted that hooks might be fired more than once in the WordPress flow for various reasons. Any actions added to these hooks will execute each time the hook is fired.
You've now learned how the two most basic action hook functions work: do_action() and add_action(). WordPress also has other functions for working with action hooks that can be useful in your plugins.
The do_action_ref_array() function works the same way as do_action() works, with a difference in how the arguments are passed. Rather than passing multiple, optional values as additional parameters, it passes an array of arguments. The array of arguments is also a required parameter. The purpose of the function is to pass an object by reference to actions added to a specific hook. This means the action can change the object itself without returning it.
<?php do_action_ref_array( $tag, $args ); ?>
$tag — The name of the action hook.
$args — An array of arguments passed to actions registered for the hook. Generally, this would be an object that actions can change.
Now take a look at a specific instance of how WordPress calls do_action_ref_array(). The following code shows the pre_get_posts action hook. WordPress executes this hook before loading posts from the database, enabling plugins to change how posts are queried.
<?php do_action_ref_array( 'pre_get_posts', array( &$this ) ); ?>
You can see that the pre_get_posts is the hook name, which is the first parameter. The second parameter in this case is an array of query arguments for pulling posts from the database. This hook enables you to execute code based on that array of arguments.
Suppose you wanted to randomly order posts on the blog home page rather than have the default ordering by the post date. You would register an action on this hook and change the order.
<?php
add_action( 'pre_get_posts', 'boj_randomly_order_blog_posts' );
function boj_randomly_order_blog_posts( $query ) {
if ( $query->is_home && empty( $query->query_vars['suppress_filters'] ) )
$query->set( 'orderby', 'rand' );
}
?>Code snippet boj-random-blog-posts.php
remove_action() enables you to remove an action that has previously been added to a hook. Typically, you would remove actions that WordPress adds by default. To remove an action, the action must have already been added using the add_action() function. If your code runs before the action is registered, the action will not be removed from the hook.
The function returns true when the action was successfully removed and false when the action could not be removed.
<?php remove_action( $tag, $function_to_remove, $priority, $accepted_args ); ?>
$tag — The name of the action hook the action you want to remove is hooked to.
$function_to_remove — The name of the function that has been added to the hook.
$priority — The priority given in the add_action() function. This defaults to a value of 10.
$accepted_args — The number of accepted arguments the action accepts. This defaults to a value of 1.
To successfully remove an action from a hook, the $tag, $function_to_remove, and $priority parameters must exactly match the parameters used in do_action(). Otherwise, the action will not be removed and remove_action() will return false.
Let's take a look at one of WordPress' default actions called rel_canonical. This action adds a canonical link between the opening <head> and closing </head> element on the site's front end.
<?php add_action( 'wp_head', 'rel_canonical' ); ?>
To remove this action, you must use the remove_action() function in your plugin. You need to define the $tag and $function_to_remove parameters. In this case, you don't need to add the $priority parameter because no priority was explicitly given in the action previously defined.
<?php remove_action( 'wp_head', 'rel_canonical' ); ?>
It is possible to remove any action added by WordPress, a plugin, or a theme within your plugin. Generally, you remove actions within WordPress. Many of its default actions are defined in the wp-includes/default-filters.php file. Browsing this file can give you a general overview of how WordPress uses action hooks.
In some plugins, you may find it necessary to remove all actions for a given tag or all actions for a given tag and priority. The remove_all_actions() function enables you to do this with a single line of code instead of multiple uses of the remove_action() function.
<?php remove_all_actions( $tag, $priority ); ?>
$tag — The name of the action hook that you want to remove all actions on.
$priority — The priority of the actions to remove. This parameter is optional and defaults to false. If you set this parameter, only actions with this specific priority will be removed.
In this next example, you remove all actions, regardless of priority, from the wp_head action hook.
<?php remove_all_actions( 'wp_head' ); ?>
If you want to remove only actions with a specific priority, you would give a value for the second parameter of $priority. To remove all actions with a priority of 1 for the wp_head hook use the following code.
<?php remove_all_actions( 'wp_head', 1 ); ?>
You should be careful when using this function. Other plugins and themes may add actions that you are unaware of. Using this may break functionality that your plugin users are expecting to work. It's usually better to be as specific as possible with your code. In most cases, you should use the remove_action() function instead.
Sometimes, you may find it necessary to check if a hook has any actions or if a specific action has been added to a hook before executing code. The has_action() function is a conditional function that gives you the ability to check both these cases.
<?php has_action( $tag, $function_to_check ); ?>
$tag — The name of the action hook you want to check for actions registered to it.
$function_to_check — The name of a function to check if it has been added to the hook. This parameter is optional and defaults to a value of false.
The return value for has_action() varies between a Boolean value and an integer. If $function_to_check is not set, the function returns true if actions are added to the hook or false if no actions are added to the hook. If $function_to_check is set and the function has been added to the hook, the priority (integer) of the action will be returned. Otherwise, a value of false will be returned.
In the next example, you display a message based on whether the wp_footer action hook has any action registered for it.
<?php
if ( has_action( 'wp_footer' ) )
echo '<p>An action has been registered for the footer.</p>';
else
echo '<p>An action hasn\'t been registered for the footer.</p>';
?>Now look at an action WordPress core adds to wp_footer. The wp_print_footer_scripts() is registered for this hook by default.
<?php add_action( 'wp_footer', 'wp_print_footer_scripts' ); ?>
If you want to display a message if that particular action were registered for the hook, you would use the following code.
<?php
if ( has_action( 'wp_footer', 'wp_print_footer_scripts' ) )
echo '<p>The wp_print_footer_scripts is registered for wp_footer.</p>';
?>The did_action() function enables your plugin to check if an action hook has already been executed or to count the number of times one has been executed. This also means that some action hooks are fired multiple times during a single page load.
<?php did_action( $tag ); ?>
$tag — Name of the action hook to check.
The function returns the number of times the hook has been fired or false if it hasn't been fired. The most common use case of the function is to check if an action hook has already been fired and execute code based on the return value of did_action().
In the next example, you define a PHP constant if the plugins_loaded action hook has already fired.
<?php
if ( did_action( 'plugins_loaded' ) )
define( 'BOJ_MYPLUGIN_READY', true );
?>WordPress has two functions for registering action hooks for the activation and deactivation of individual plugins. Although these are technically functions to create custom hooks, both functions are covered in Chapter 2, "Plugin Foundation," in complete detail.
WordPress has many action hooks, but some of them are used more often than others. Knowing what these hooks are can help you lay down the groundwork for your plugins.
For plugin developers, the plugins_loaded action hook is probably the most important hook. It is fired after most of the WordPress files are loaded but before the pluggable functions and WordPress starts executing anything. In most plugins, no other code should be run until this hook is fired. plugins_loaded is executed when all the user's activated plugins have been loaded by WordPress. It is also the earliest hook plugin developers can use in the loading process.
A WordPress plugin should do its setup on this hook. Other actions should also be added within the callback function used on this hook.
In the following example, you use the boj_example_footer_message action you created in the previous section. Rather than calling it separately, add it to your setup action, which is hooked to plugins_loaded.
<?php
add_action( 'plugins_loaded', 'boj_footer_message_plugin_setup' );
function boj_footer_message_plugin_setup() {
/* Add the footer message action. */
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
}
function boj_example_footer_message() {
echo 'This site is built using <a href="http://wordpress.org"
title="WordPress publishing platform">WordPress</a>.';
}
?>It is good practice to create a setup function and hook it to plugins_loaded. By doing this, you can ensure that you don't inadvertently trigger any errors from a specific WordPress function not being loaded.
The init hook is fired after most of WordPress is set up. WordPress also adds a lot of internal functionality to this hook such as the registration of post types and taxonomies and the initialization of the default widgets.
Because nearly everything in WordPress is ready at this point, your plugin will probably use this hook for anything it needs to do when all the information from WordPress is available.
In the following example, you add the ability for users to write an excerpt for pages. You would do this on init because the "page" post type is created at this point using the add_post_type_support() function (see Chapter 11, "Extending Posts").
<?php
add_action( 'init', 'boj_add_excerpts_to_pages' );
function boj_add_excerpts_to_pages() {
add_post_type_support( 'page', array( 'excerpt' ) );
}
?>The admin_menu hook is called only when an administration page loads. Whenever your plugin works directly in the admin, you would use this hook to execute your code.
The next example adds a sub-menu item labeled BOJ Settings to the Settings menu in the WordPress admin (for more on this, see Chapter 7, "Plugin Settings").
<?php
add_action( 'admin_menu', 'boj_admin_settings_page' );
function boj_admin_settings_page() {
add_options_page(
'BOJ Settings',
'BOJ Settings',
'manage_options',
'boj_admin_settings',
'boj_admin_settings_page'
);
}
?>The template_redirect action hook is important because it's the point where WordPress knows which page a user is viewing. It is executed just before the theme template is chosen for the particular page view. It is fired only on the front end of the site and not in the administration area. This is a good hook to use when you need to load code only for specific page views.
In the next example, you load a style sheet file only for a singular post view.
<?php
add_action( 'template_redirect', 'boj_singular_post_css' );
function boj_singular_post_css() {
if ( is_singular( 'post' ) ) {
wp_enqueue_style(
'boj-singular-post',
'boj-example.css',
false,
0.1,
'screen'
);
}
}
?>On the front end of the site, WordPress themes call the wp_head() function, which fires the wp_head hook. Plugins use this hook to add HTML between the opening <head> tag and its closing </head>.
In the following example, you add a meta description on the front page of the site using the site's description.
<?php
add_action( 'wp_head', 'boj_front_page_meta_description' );
function boj_front_page_meta_description() {
/* Get the site description. */
$description = esc_attr( get_bloginfo( 'description' ) );
/* If a description is set, display the meta element. */
if ( !empty( $description ) )
echo '<meta name="description" content="' . $description . '" />';
}
?>Many plugins incorrectly use the wp_head action hook to add JavaScript to the header when they should be using the wp_enqueue_script() function (see Chapter 12, "JavaScript and AJAX"). The only time JavaScript should be added to this hook is when it's not located in a separate JavaScript file.
Filter hooks are much different than action hooks. They enable you to manipulate the output of code. Whereas action hooks enable you to insert code, filter hooks enable you to overwrite code that WordPress passes through the hook. Your function would "filter" the output.
To grasp the concept of filter hooks, you must first understand how the apply_filters() WordPress function works.
<?php apply_filters( $tag, $value ); ?>
$tag — The name of the filter hook.
$value — The parameter passed to any filters added to the hook. The function can also take in any number of extra $value parameters to pass to filters.
It is important to note here that $value must be returned back to WordPress when writing a filter.
Here is an example of a filter hook from the core WordPress code.
<?php apply_filters( 'template_include', $template ); ?>
In this example, template_include is name of the filter hook. $template is a file name that can be changed through filters registered for the filter hook.
A filter is a function registered for a filter hook. The function itself would take in at least a single parameter and return that parameter after executing its code. Without a filter, filter hooks don't do anything. They exist so that plugin developers can change different variables. This can be anything from a simple text string to a multidimensional array.
When a filter hook is called by the apply_filters() function, any filters registered for the hook are executed. To add a filter, use the add_filter() function.
<?php add_filter( $tag, $function, $priority, $accepted_args ); ?>
$tag — The name of the hook you want to register your filter for.
$function — The function name of the filter that you create to manipulate the output.
$priority — An integer that represents in what order your filter should be applied. If no value is added, it defaults to 10.
$accepted_args — The number of parameters your filter function can accept. By default this is 1. Your function must accept at least one parameter, which will be returned.
You can add multiple filters to the same filter hook. Other plugins and WordPress can also add filters to the hook. Filter hooks aren't limited to a single filter. It is important to note this because each filter must always return a value for use by the other filters. If your function doesn't return a value, you risk breaking the functionality of both WordPress and other plugins.
Now look at the wp_title filter hook in WordPress, which is a filter hook responsible for the <title> element on a page.
<?php apply_filters( 'wp_title', $title, $sep, $seplocation ); ?>
wp_title — The name of the hook.
$title — A string and the value that you want to filter and return back to WordPress.
$sep — A string that tells you what the separator should be between elements in the <title> element.
$seplocation — The location of the separator. In the next example, you don't use it.
You're now going to write a function that filters the output of $title by appending the site's name to the end of page title.
<?php
add_filter( 'wp_title', 'boj_add_site_name_to_title', 10, 2 );
function boj_add_site_name_to_title( $title, $sep ) {
/* Get the site name. */
$name = get_bloginfo( 'name' );
/* Append the name to the $title variable. */
$title .= $sep . ' ' . $name;
/* Return the title. */
return $title;
}
?>Take a look at the line telling WordPress to add a filter to wp_title.
<?php add_filter( 'wp_title', 'boj_add_site_name_to_title', 10, 2 ); ?>
It says that you want to add a filter named boj_add_site_name_title_title to the wp_title filter hook. You set a priority of 10 and tell your filter to accept two parameters.
The boj_add_site_name_to_title() function manipulates the $title parameter and returns it back to WordPress. The $sep parameter can be used within the function but is not returned.
Aside from the apply_filters() and add_filter() functions covered in the previous sections of this chapter, WordPress has several other functions for working with filter hooks.
The apply_filters_ref_array() function works nearly the same as apply_filters(). One major difference is what parameters are passed. Rather than accepting multiple values, it accepts an array of arguments. Both parameters are required. It is also important to note that the $args parameter should be passed by reference rather than value.
<?php apply_filters_ref_array( $tag, $args ); ?>
$tag — The name of the filter hook.
$args — An array of arguments to pass to filters registered for the hook.
Suppose you have a complex database query that you need to perform to load posts for the front page of the site that normal WordPress functions don't enable. WordPress has a filter hook called posts_results that enables you to change this. Here's what it looks like in the WordPress core code.
<?php
$this->posts =
apply_filters_ref_array(
'posts_results', array( $this->posts, &$this )
);
?>This filter hook passes an array of post objects to any filters registered for it. Using the following example, you completely overwrite this array of post objects and replace it with a custom set. By default, WordPress queries posts of the post post type. You change this to list posts of the page post type on the site home page.
The code example uses the wpdb class, which is covered in more detail in Chapter 6, "Plugin Security."
<?php
add_filter( 'posts_results', 'boj_custom_home_page_posts' );
function boj_custom_home_page_posts( $results ) {
global $wpdb, $wp_query;
/* Check if viewing the home page. */
if ( is_home() ) {
/* Posts per page. */
$per_page = get_option( 'posts_per_page' );
/* Get the current page. */
$paged = get_query_var( 'paged' );
/* Set the $page variable. */
$page = ( ( 0 == $paged || 1 == $paged ) ? 1 : absint( $paged ) );
/* Set the number of posts to offset. */
$offset = ( $page - 1 ) * $per_page . ', ';
/* Set the limit by the $offset and number of posts to show. */
$limits = 'LIMIT '. $offset . $per_page;
/* Get results from the database. */
$results = $wpdb->get_results( "
SELECT SQL_CALC_FOUND_ROWS $wpdb->posts.*
FROM $wpdb->posts
WHERE 1=1
AND post_type = 'page'
AND post_status = 'publish'ORDER BY post_title ASC
$limits
" );
}
return $results;
}
?>Code snippet boj-custom-home-page.php
The remove_filter() function enables plugins to remove filters that have been previously registered for a filter hook. To successfully remove a filter, this function must be called after a filter has been registered using the add_filter() function.
<?php remove_filter( $tag, $function_to_remove, $priority, $accepted_args ); ?>
$tag — The name of the filter hook to remove a filter from.
$function_to_remove — The function to remove from the filter hook.
$priority — The priority previously used in add_filter() to register the filter. This parameter defaults to 10.
$accepted_args — The number of accepted arguments previously declared in the add_filter() called to register the filter. This parameter defaults to 1.
The function returns true when the filter is successfully removed and returns false when the removal is unsuccessful. The $tag, $function_to_remove, and $priority parameters must also match the parameters set with add_filter() exactly. Otherwise, the filter will not be removed.
Now look at WordPress' default filters defined in wp-includes/default-filters.php. One interesting filter is a function called wpautop(), which converts double line breaks into HTML paragraphs. It is executed on several hooks in the core code. Here's how one instance of it looks in the core WordPress code.
<?php add_filter( 'the_content', 'wpautop' ); ?>
This applies the wpautop() filter to a post's content, converting each double line break of the post into a paragraph. You may have a client project that requires that specific language and formatting rules be followed. For example, the client may not want their content to have paragraphs automatically formatted. You would use the remove_filter() function to remove the filter from the the_content hook.
<?php remove_filter( 'the_content', 'wpautop' ); ?>
In the previous code, you had to define the $tag and $function_to_remove parameters to ensure that the correct filter was removed from the correct hook. Since the original action defined no priority and the default is 10, you didn't have to define the $priority parameter.
In some plugins, you may need to remove all filters from a specific filter hook or remove filters with a particular priority from a filter hook. The remove_all_filters() function enables you to do this with a single line of code.
<?php remove_all_filters( $tag, $priority ); ?>
$tag — Name of the filter hook to remove all filters from.
$priority — Priority of the filters to remove from the filter hook. This parameter is optional. If not set, all filters will be removed from the hook.
Suppose you want to remove all default formatting such as auto-paragraphs and the conversion of certain characters to their character entity equivalents for post content. WordPress adds several filters to the the_content filter hook that handles this automatically. To remove all these filters, use the remove_all_filters() with a single parameter with a value of the_content.
<?php remove_all_filters( 'the_content' ); ?>
If you want to remove only filters with a specific priority, you need to set the second parameter. In the next example, you remove filters for the_content with the priority of 11.
<?php remove_all_filters( 'the_content', 11 ); ?>
The has_filter() function enables plugins to check if any filters have been registered for a filter hook or if a specific filter has been registered for the hook.
<?php has_filter( $tag, $function_to_check ); ?>
$tag — Name of the filter hook to check whether it has any registered filters.
$function_to_check — A specific function to check against the filter. This parameter is optional.
The function returns false if no filter is found for the given hook. It returns true if a filter is found. However, if the $function_to_check parameter is set, it returns the priority of the filter.
Using the following code, you can check if a filter has been added to the_content. The code prints a message based on the return value of has_filter().
<?php
if ( has_filter( 'the_content' ) )
echo 'The content filter hook has at least one filter.';
else
echo 'The content filter hook has no filters.';
?>If you want to check for a specific filter registered for a filter hook, you need to use the $function_to_check parameter. Suppose you want to check if the WordPress auto-paragraph functionality was applied to the post content. With the following code, you can print a message if this is true.
<?php
if ( has_filter( 'the_content', 'wpautop' ) )
echo 'Paragraphs are automatically formatted for the content.';
?>The current_filter() function returns the name of the filter hook currently executed. However, it doesn't just work with filter hooks; it applies to action hooks as well, so it returns the name of the current action or filter hook. This function is especially useful if you use a single function for multiple hooks but need the function to execute differently depending on the hook currently firing.
Suppose you have a client that needs to remove specific words from post titles and post content. The client wants to allow some words in the post title, but the allowed set of words is slightly different for the post content. You can use a single function to filter both the_content and the_title while using the current_filter() function to set the words based on which hook is currently executed.
Using the following code, you can set an array of unwanted words depending on the case and replace them with "Whoops!" in the text.
<?php
add_filter( 'the_content', 'boj_replace_unwanted_words' );
add_filter( 'the_title', 'boj_replace_unwanted_words' );
function boj_replace_unwanted_words( $text ) {/* If the_content is the filter hook, set its unwanted words. */
if ( 'the_content' == current_filter() )
$words = array( 'profanity', 'curse', 'devil' );
/* If the_title is the filter hook, set its unwanted words. */
elseif ( 'the_title' == current_filter() )
$words = array( 'profanity', 'curse' );
/* Replace unwanted words with "Whoops!" */
$text = str_replace( $words, 'Whoops!', $text );
/* Return the formatted text. */
return $text;
}
?>Code snippet boj-replace-unwanted-words.php
Often, you'll need to write a function that returns a common value to a filter hook such as true, false, or an empty array. You might even be tempted to use PHP's create_function() function to quickly return a value.
WordPress has several functions for handling scenarios such as this. With the next example code, you disable the user contact methods, which are a list of <input> boxes on individual user edit screens in the WordPress admin. To disable these boxes, you would need to return an empty array. Normally, you'd have to add the filter hook call and code the function.
<?php
add_filter( 'user_contactmethods', 'boj_return_empty_array' );
function boj_return_empty_array() {
return array();
}
?>Writing the code for that isn't so bad if doing it once or twice. However, it almost seems silly to have to write an entire function to return an empty array. WordPress makes this much easier. Because you're simply disabling these boxes, you can use WordPress's __return_empty_array() function as a filter for quickly returning an empty array, replacing the previous code snippet with the following.
<?php add_filter( 'user_contactmethods', '__return_empty_array' ); ?>
Using __return_empty_array is important here. WordPress will expect the value returned to be an array, so returning something other than an array would result in a PHP error.
The WordPress functions for quickly returning values can come in handy when developing plugins. It's always important to check the core code to see what value is expected after filters have been applied. Each scenario will call for a specific return value type:
__return_false — Returns the Boolean value of false.
__return_true — Returns the Boolean value of true.
__return_empty_array — Returns an empty PHP array.
__return_zero — Returns the integer 0.
These are simply a few functions WordPress makes available for use within plugins. If you find yourself writing one-line functions just to return a single value to a filter hook, you have the option of creating your own functions for handling this if the previous list of functions doesn't cover your use case.
WordPress has hundreds of filter hooks for use by plugin developers. Narrowing these down to a small list of some commonly used hooks doesn't come close to accurately representing what a plugin can accomplish by using the hook system. In this section, you learn how to use some of the more common filter hooks that plugin developers use in their plugins.
If there's one filter hook that plugin authors use more than any other, it is the_content. Without content, a site would be essentially useless. It is the most important thing displayed on the site, and plugins use this hook to add many features to a site.
The the_content hook passes a post's content to any filters registered for it. Filters then manipulate the content, usually for extra formatting or to append additional information about the post.
With the next code example, you append a list of related posts by post category to the_content for a reader to see when viewing a single post.
<?php
add_filter( 'the_content', 'boj_add_related_posts_to_content' );
function boj_add_related_posts_to_content( $content ) {
/* If not viewing a singular post, just return the content. */
if ( !is_singular( 'post' ) )
return $content;
/* Get the categories of current post. */
$terms = get_the_terms( get_the_ID(), 'category' );
/* Loop through the categories and put their IDs in an array. */$categories = array();
foreach ( $terms as $term )
$categories[] = $term->term_id;
/* Query posts with the same categories from the database. */
$loop = new WP_Query(
array(
'cat__in' => $categories,
'posts_per_page' => 5,
'post__not_in' => array( get_the_ID() ),
'orderby' => 'rand'
)
);
/* Check if any related posts exist. */
if ( $loop->have_posts() ) {
/* Open the unordered list. */
$content .= '<ul class="related-posts">';
while ( $loop->have_posts() ) {
$loop->the_post();
/* Add the post title with a link to the post. */
$content .= the_title(
'<li><a href="' . get_permalink() . '">',
'</a></li>',
false
);
}
/* Close the unordered list. */
$content .= '</ul>';
/* Reset the query. */
wp_reset_query();
}
/* Return the content. */
return $content;
}
?>Code snippet boj-related-posts.php
Post titles are almost as important as the post content, which makes the_title a popular filter hook for use. You can use this hook to add information or overwrite completely.
One useful filter to use for the_title is a function to strip HTML tags from it. Users sometimes add tags here that can mess up the formatting of the title on output. Using the following code, you can strip all tags a user might use when writing a post title.
<?php
add_filter( 'the_title', 'boj_strip_tags_from_titles' );
function boj_strip_tags_from_titles( $title ) {
$title = strip_tags( $title );
return $title;
}
?>The comment_text hook is often a useful filter hook because comments typically play a large role for blogs and other types of sites.
With the next code example, you check if a comment was made by a registered user on the site. If the user is registered, you can append a paragraph that prints the user's role for the site (see Chapter 8, "Users").
<?php
add_filter( 'comment_text', 'boj_add_role_to_comment_text' );
function boj_add_role_to_comment_text( $text ) {
global $comment;
/* Check if comment was made by a registered user. */
if ( $comment->user_id > 0 ) {
/* Create new user object. */
$user = new WP_User( $comment->user_id );
/* If user has a role, add it to the comment text. */
if ( is_array( $user->roles ) )
$text .= '<p>User Role: ' . $user->roles[0] . '</p>';
}
return $text;
}
?>template_include is a sort of catchall filter hook for many other, more specific filter hooks.
front_page_template
home_template
single_template
page_template
attachment_template
archive_template
category_template
tag_template
author_template
date_template
archive_template
search_template
404_template
index_template
It is used after the theme template file for the current page has been chosen. WordPress chooses a template based on the page currently viewed by a reader. You can add a filter for each of the individual filter hooks or filter them all at the end with the template_include hook.
Suppose you wanted to build a custom template hierarchy to allow themes to use templates based on your plugin's criteria instead of the normal WordPress template hierarchy. The template_include and the other hooks in the previous list enable you to do this.
Using the next example code, you check if a template exists for single posts by category. By default, WordPress looks for a single.php file first and then falls back to index.php if it doesn't exist. Your function looks for a file called single-category-$slug.php ($slug is the category slug), so if a user has a category with the slug of "art" and a template named single-category-art.php in their theme, this file will be used in lieu of single.php.
<?php
add_filter( 'single_template', 'boj_single_template' );
function boj_single_template( $template ) {
global $wp_query;
/* Check if viewing a singular post. */
if ( is_singular( 'post' ) ) {
/* Get the post ID. */
$post_id = $wp_query->get_queried_object_id();
/* Get the post categories. */
$terms = get_the_terms( $post_id, 'category' );
/* Loop through the categories, adding slugs as part of the file name. */
$templates = array();
foreach ( $terms as $term )
$templates[] = "single-category-{$term->slug}.php";/* Check if the template exists. */
$locate = locate_template( $templates );
/* If a template was found, make it the new template. */
if ( !empty( $locate ) )
$template = $locate;
}
/* Return the template file name. */
return $template;
}
?>Code snippet boj-single-template.php
Throughout this chapter, you've seen many examples of using action and filter hooks with PHP functions. When adding a method of a class as an action or filter, the format of the calls to add_action() and add_filter() is slightly different.
In general, plugins most often use functions as actions and filters rather than class methods. However, there will be cases in which using a class will be beneficial to your plugin, and you will need to know how to register methods for hooks from within the class.
Take a look at a basic function registered for an action hook, which was covered in detail in the section on actions earlier in the chapter.
<?php add_action( $tag, $function_to_add ); ?>
When using a method such as $function_to_add from within a class, you must change $function_to_add to an array with &$this as the first argument and the method name as the second argument.
<?php add_action( $tag, array( &$this, $method_to_add ) ); ?>
The same is true for filter hooks as well. A function added to a filter hook would normally look like this:
<?php add_filter( $tag, $function_to_add ); ?>
When using a class method as a filter, you must also change the $function_to_add parameter.
<?php add_filter( $tag, array( &$this, $method_to_add ) ); ?>
In the following example, you build a class that has a constructor method, a method used as an action, and a method used as a filter. The add_filters() method checks if the reader is currently viewing a singular post view. If true, the content() method appends the last modified date of the post to the post content.
<?php
class BOJ_My_Plugin_Loader {
/* Constructor method for the class. */
function BOJ_My_Plugin_Loader() {
/* Add the 'singular_check' method to the 'template_redirect' hook. */
add_action( 'template_redirect', array( &$this, 'singular_check' ) );
}
/* Method used as an action. */
function singular_check() {
/* If viewing a singular post, filter the content. */
if ( is_singular() )
add_filter( 'the_content', array( &$this, 'content' ) );
}
/* Method used as a filter. */
function content( $content ) {
/* Get the date the post was last modified. */
$date = get_the_modified_time( get_option( 'date_format' ) );
/* Append the post modified date to the content. */
$content .= '<p>Post last modified: ' . $date . '</p>';
/* Return the content. */
return $content;
}
}
$boj_myplugin_loader = new BOJ_My_Plugin_Loader();
?>Code snippet boj-class-hooks.php
Not only can plugins take advantage of the core code's built-in hooks, but they can also create custom hooks for use by other plugins and themes. Doing so can be especially beneficial with code when it's okay to change the output of that code.
Your plugin would use one of four available functions for creating custom action hooks.
do_action()
do_action_ref_array()
apply_filters()
apply_filters_ref_array()
You would use the first two functions for creating custom action hooks and the next two functions for creating custom filter hooks.
Custom hooks make your plugin more flexible, allow it to be extended by others, and gives you the ability to hook into the execution of various processes throughout your plugin within the plugin itself.
Using custom hooks also keep users from editing your work directly. The importance of this is that when you provide an update for the plugin, users won't lose any modifications they've made.
In this custom action hook example, you create a plugin setup function. The function defines a constant that can be altered. Other plugins may also execute any code they want on the hook. You provide it so that they have an opportunity to run code at that point.
<?php
add_action( 'plugins_loaded', 'boj_myplugin_setup' );
function boj_myplugin_setup() {
/* Allow actions to fire before anything else. */
do_action( 'boj_myplugin_setup_pre' );
/* Check if the root slug is defined. */
if ( !defined( 'BOJ_MYPLUGIN_ROOT_SLUG' ) )
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'articles' );
}
?>Other plugins or themes may hook into boj_myplugin_setup_pre and execute any function. Suppose you want to change the BOJ_MYPLUGIN_ROOT_SLUG constant from "articles" to "papers." You can create a custom action to add to the hook.
<?php
add_action( 'boj_myplugin_setup_pre', 'boj_define_myplugin_constants' );
function boj_define_myplugin_constants() {
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'papers' );
}
?>Suppose you have a function that displays a list of posts given a specific set of arguments. You may want to grant others the ability to filter the arguments and filter the final output.
In this example, you write a function that lists the top 10 posts by the number of comments a post has received. The function enables users to filter the arguments for grabbing the posts from the database and enables them to filter the final HTML output of the list.
<?php
function boj_posts_by_comments() {
/* Default arguments. */
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'order' => 'DESC',
'orderby' => 'comment_count'
);
/* Apply filters to the arguments. */
$args = apply_filters( 'boj_posts_by_comments_args', $args );
/* Set up the output variable. */
$out = '';
/* Query posts from the database by the given arguments. */
$loop = new WP_Query( $args );
/* Check if posts are found. */
if ( $loop->have_posts() ) {
/* Open the unordered list. */
$out .= '<ul class="posts-by-comments">';
/* Loop through the posts. */
while ( $loop->have_posts() ) {
$loop->the_post();
/* Add the post title to the list. */
$out .= the_title( '<li>', '</li>', false );
}
/* Close the unordered list. */
$out .= '</ul>';
}
/* Apply filters to the final output. */
$out = apply_filters( 'boj_posts_by_comments', $out );/* Display the HTML. */
echo $out;
}
?>To filter the arguments, add a filter to boj_posts_by_comments_args. Suppose you want to change the default number of 10 posts to 15. You add a custom filter for this.
<?php
add_filter( 'boj_posts_by_comments_args', 'boj_change_posts_by_comments_args' );
function boj_change_posts_by_comments_args( $args ) {
/* Change the value of the posts_per_page array key. */
$args['posts_per_page'] = 15;
/* Return the $args parameter. */
return $args;
}
?>To filter the final HTML output, add a filter to boj_posts_by_comments. Suppose you want to change the unordered list to an ordered list with a custom filter.
<?php
add_filter( 'boj_posts_by_comments', 'boj_change_posts_by_comments' );
function boj_change_posts_by_comments( $out ) {
/* Change the opening <ul> to an <ol>. */
$out = str_replace( '<ul ', '<ol ', $out );
/* Change the closing </ul> to an </ol>. */
$out = str_replace( '</ul>', '</ol>', $out );
/* Return the filtered HTML. */
return $out;
}
?>It would be nearly impossible to give a complete list of all the available hooks in WordPress. In earlier sections of this chapter, you learned about some of the more common action and filter hooks, but these sections cover only a small sampling of what WordPress has to offer.
New hooks are always added with new versions of WordPress. Keeping track of changes in the core code from version to version can help you stay on top of new hooks that you can use within your plugins.
As a plugin developer, you should become familiar with the core WordPress code. Looking for hooks is a great way to start familiarizing yourself with how WordPress works. There's no better process for understanding how PHP code works than actually looking at the code and following each statement made within the code.
An easy way to search for hooks is to open a file from the wordpress folder in your preferred text editor and run a text search for one of four function names.
do_action
do_action_ref_array
apply_filters
apply_filters_ref_array
Those are the functions you learned to use earlier in the chapter. Each function creates a hook, so by searching for one of those strings, you can find new hooks in WordPress.
When searching for hooks throughout the core WordPress code, you will come across what's known as "variable hooks." Normally, hook names are a static string of text. However, variable hook names change based on a specific variable.
A good example of this is the load-$pagenow action hook. The $pagenow variable changes depending on the WordPress admin page currently viewed. The hook looks like the following in the core code.
<?php do_action( "load-$pagenow" ); ?>
The $pagenow variable will become the page name being viewed. For example, the hook for the new post page in the admin would be load-post-new.php and the hook on the edit posts screen would be load-post.php. This enables plugins to run code only for specific page views in the admin.
WordPress has several action and filter hooks with variables as part of the hook name. Generally, these hook names change to provide context to plugin developers so that they can execute code only when specific circumstances are met. It is important to keep this in mind when searching for just the right hook to use in your plugin.
Although searching for hooks within the core code can be beneficial to your learning experience, sometimes you may find it easier to access some of the publicly available reference lists on the Web. These lists can sometimes save you time and may also have descriptions about the hook.
WordPress has both an official action and filter hook reference list in its Codex.
http://codex.wordpress.org/Plugin_API/Action_Reference
http://codex.wordpress.org/Plugin_API/Filter_Reference
Chapter 18, "The Developer Toolbox," covers more materials and tools to help plugin developers.
Hooks are the most important aspect of building plugins for WordPress. Each time you start a new plugin project, you hook your plugin's functions into the WordPress action and filter hooks. You can use everything you learned throughout this chapter in the following chapters of the book because hooks play such an integral part in plugin development.
Now that you can fully grasp how hooks work, it's time to start building plugins.