The Foundation: do_action
do_action creates the hook for things to hang. This is at the core of WordPress and even frameworks like Genesis and many other themes and plugins. do_action is the first domino in the chain of events with hooks. However, alone, it means nothing and does nothing. Simply, it tells WordPress to search to see if any functions are attached to it to fire. So do_actions will look something like this:
[php]<?php do_action( 'my-home' ); [/php]
It can also take and pass variables:
[php]<?php do_action( 'my-home' , $var1 , $var2 ); [/php]
The Difference between add_action and add_filter
The difference is primarily and technically semantic. Technically speaking, you can use them interchangeably, but it wouldn't follow code common sense or code "mentality" as one writer said. Filters should filter information, thus receiving information/data, applying the filter and returning information/data, and then used. However, filters are still action hooks.
WordPress defines add_filter as "Hooks a function to a specific filter action," and add_action as "Hooks a function on to a specific action."
add_action
Now, add_actions are a bit different. They hang items on the do_action hook and $priority determines the order.
Say, for example, you have this in your functions.php file:
[php]<?php
add_action( 'hook' , 'bob' );
add_action( 'hook' , 'andy' );
[/php]
The general order is "chronological" meaning if action 'bob' appears (coded/read by the server) before action 'andy', then the action hook 'bob' will fire first. However, with $priority, this order can be interrupted and changed. So...
[php]<?php
add_action( 'hook' , 'bob' , 10 );
add_action( 'hook' , 'andy' , 5 );
[/php]
With add_actions, variables and information may be passed back and forth as needed in the various functions as need. It should also be noted that not all actions are void of arguments or parameters. Some actions do have parameters, so:
[php]<?php add_action( $tag, $function_to_add, $priority, $accepted_args ); ?>[/php]
So, it would look something like this:
[php]<?php add_action( 'hook_name' , 'my_function_name' , 10 , 2 ); ?>[/php]
Extended Version Example
[php]<?php
function echo_comment_id( $comment_ID )
{
echo "I just received $comment_ID";
}
add_action( 'comment_id_not_found', 'echo_comment_id', 10, 1 );[/php]
add_filter
Now WordPress (and my theme) passes much of this page through various filters that check, validate, correct, and even modify various parts. Once it passes through the filter, the information then is applied to an action.
So here is the add_filter function:
[php]<?php add_filter( $tag, $function_to_add, $priority, $accepted_args ); ?>[/php]
This generally looks something like this:
[php]<?php
add_filter( 'filter_name' , 'my_filter_function_name' , 10 , 3 );
function filter_name( $val, $attr, $content = null ) {
//do something
}
?>[/php]
Just like add_actions go with do_actions, the same is true for filters logically speaking. With add_filter you must have apply_filters. Without the filter being called or applied then the filter means nothing, logically.
So, logicaly, in our example with filter 'filter_name', this would modify content/information that is coded like this:
[php]<?php
// Allow plugins/themes to override the default caption template.
$output = apply_filters( 'filter_name', $output , $val, $attr , $content );
if ( $output != '' )
return $output;
[/php]
So, the code is basically saying, “Take the value of the `$output` variable, apply any filters attached to the ‘filter_name’ hook passing the variables $val, $attr, $content to the filter function (whatever that may be and if it accepts them), and assign the filtered value back to the `$output` variable”.
However, technically speaking, PHP is rather forgiving and instead of filtering anything, it can function like add_action, adding the filter to a do_action, not filtering anything, which adds to the confusion. So if you have the following, it will work (though not good form):
[php]</php
do_action( 'my_action' );
add_filter( 'my_action' , 'my_function');
function my_function() {
//do something
}[/php]
Since the add_filter did not have any filters being applied, it worked as an add_action.
glen says
can both do_action and add_action work in plugin files. not in themes / functions.php ?
Thanks
Glen
Travis Smith says
No, they can be done in theme files. Frameworks like Genesis and Thesis run on these hooks.
Sajid Z says
I think add_action can be called in a plugin file. Like if you want to perform some function on init within a plugin file, You have to use add_action. I worked on a export plugin once and i have used it due to headers being sent for file download.
Gil Yoder says
I am a little confused by the descriptions I’ve read here and at other places that describe do_action as a function that “creates a hook” and the conclusion that it is the “first domino in the chain of events with hooks.” When I first saw do_action described this way, I thought it was a joke that someone was playing, but after seeing it in numerous other sites, I realize that this is really how programmers are describing it. To me though the description is confusing and misleading.
It must be that these methods are being described from a conceptual point of view rather than from a concrete point of view. Conceptually when a programmer uses do_action he is creating a hook that other programmers can use to actions into his function, but that is what the programmer is doing; it is not the do_action method creating the hook. The method rather is executing (or doing) an action if an action exists for it to do.
If any method is creating a hook, then the hook is being created by add_action, not do_action, because in order for do_action to do anything useful, at least one add_action must have been called for the hook that do_action will use to execute the action.
The confusion that comes from describing do_action as creating a hook can be illustrated by some of your code. At the bottom of your post you have code that calls “do_action( ‘my_action’ ),” then “add_filter( ‘my_action’ , ‘my_function’)” to illustrate that PHP is forgiving and that filters can be used like actions. But in your code because add_filter follows do_action, add_filter does not impact the behavior of do_action. Even it if were changed to add_action it would have no impact. Before do_action can have any impact add_action needs to be called first to give do_action something to “do.”
Isn’t this right?
Travis Smith says
Yes, programmers must speak conceptually to communicate to a broader audience simply because of the self-taught, DIYer, people don’t really understand. Yes technically, do_action() calls another function _wp_call_all_hook() that uses the PHP function call_user_func_array() to call a variable function. However, it is still correct to say that do_action essentially creates the hook.
In my personal opinion, I believe in modular functional code based on a framework created by do_action(). You’re right in that the way my code was written in that post is not the best representation. And it is true that if you have do_action() followed by add_action() the add_action() will not be executed. Here’s what typically happens in WordPress. Take a home.php example. Here’s an example of a home.php using hooks.
[php]
<?php get_header();
do_action( ‘wps_home_before_banner’ ); ?>
<div id="banner">
<?php do_action( ‘wps_banner’ ); ?>
</div><!– end #banner –>
<div class="banner-shadow"></div>
<?php
do_action( ‘wps_home_before’ ); ?>
<div id="content-sidebar-wrap">
<div id="content">
<?php do_action( ‘wps_content’ ); ?>
</div>
<?php get_sidebar(); ?>
</div>
<?php do_action( ‘genesis_home_after’ );
get_footer();
[/php]
So in this code, I’ve hardcoded a frame with some hooks to write modular code. While I still do not believe this to be the best method, it is how more and more themes are operating. As you can see I have no add_action() statements here. In other files (e.g., functions.php, other theme files, a plugin, etc.) I would have the functions that hook into this to create the home page. So consider a home-functions.php that looks something like this:
[php]<?php
add_action( ‘wps_banner’, ‘wps_do_banner’ );
/**
* Outputs widgeted slider area
*/
function wps_do_banner() {
?>
<div class="slider">
<?php if ( ! dynamic_sidebar(‘home-slider’ ) ) : ?>
<div class="widget">
<h3><?php _e( ‘Home Slider’, ‘genesis’ ); ?></h3>
<p><?php _e( ‘Designed for a Slider’, ‘genesis’ ); ?></p>
</div>
<?php endif; ?>
</div><!– end .slider –>
<?php
}
add_action( ‘wps_banner’, ‘wps_do_search’ );
/**
* Outputs widgeted search area
*/
function wps_do_search() {
?>
<div class="home-search">
<h3><?php _e( ‘Homes for Sale’ , ‘genesis’ ); ?></h3>
<?php if ( !dynamic_sidebar( ‘home-search’ ) ) : ?>
<div class="widget">
<h3><?php _e(‘Home Search Area’ , ‘genesis’ ); ?></h3>
<p><?php _e(‘Designed for the MLS Home Search Widget’ , ‘genesis’ ); ?></p>
</div>
<?php endif; ?>
</div><!– end .home-search –>
<?php
}
add_action( ‘wps_banner’, ‘wps_clear’ );
/**
* Outputs clearfix
*/
function wps_clear() {
echo ‘<div class="clear"></div>’;
}
add_action( ‘wps_banner’, ‘wps_banner_bottom’ );
/**
* Outputs widgeted call of actions area
*/
function wps_banner_bottom() {
?>
<?php if ( is_active_sidebar( ‘banner-bottom’ ) ) : ?>
<div class="bottom-widget">
<div class="wrap">
<?php if ( ! dynamic_sidebar( ‘banner-bottom’ ) ) : ?>
<div class="widget">
<h3><?php _e(‘Widget Area’, ‘genesis’ ); ?></h3>
<p><?php _e(‘Designed for Call of Actions or Featured Listings’, ‘genesis’ ); ?></p>
</div>
<?php endif; ?>
</div><!– end .wrap –>
</div><!– end .bottom-widget –>
<?php
endif;
}
add_action( ‘wps_content’, ‘wps_do_feature_content’ );
/**
* Outputs widgeted feature content area
*/
function wps_do_feature_content() {
?>
<?php if ( ! dynamic_sidebar( ‘Home’ ) ) : ?>
<div class="widget">
<h3><?php _e( ‘Widget Area’ , ‘genesis’ ); ?></h3>
<p><?php _e( ‘Designed for Call of Actions or Featured Listings’, ‘genesis’ ); ?></p>
</div>
<?php endif; ?>
}
[/php]
Now in a framework like Genesis, this becomes much simpler because these hooks pre-exist inside the genesis() function call, so hardcoding a home.php file is not necessary. So the home.php file would look more like the second part of modular function code than anything. So…
[php]<?php
// Remove Breadcrumbs
remove_action(‘genesis_before_loop’, ‘genesis_do_breadcrumbs’);
// Add Banner
add_action( ‘genesis_after_header’, ‘wps_do_banner’ );
//previous code
// Add Search widget
add_action( ‘genesis_after_header’, ‘wps_do_search’ );
//previous code
add_action( ‘genesis_after_header’, ‘wps_clear’ );
//previous code
add_action( ‘genesis_after_header’, ‘wps_banner_bottom’ );
//previous code
// Add Optional Features Area
add_action( ‘genesis_before_loop’, ‘wps_do_feature_content’ );
//previous code
genesis();
[/php]
Even using genesis(); I am calling all the add_actions prior to the execution of the do_actions. Does this clarify a bit more?
Julian says
Hi Travis, thanks for the article! By the way the syntax highlighting seems to be out of whack. Just letting you know 🙂