wp-includes/plugin.php:487Fire an action hook with do_action() so every callback registered via add_action() runs, receiving any extra arguments you pass. Calling it with a new hook name creates the hook, which is how plugins and themes expose their own extension points; there is no return value.
Calls the callback functions that have been added to an action hook.
example_action above.
*
* - 'example_action' is the action hook.
* - $arg1 and $arg2 are the additional arguments passed to the callback.
do_action( 'example_action', $arg1, $arg2 );$hook_namestring$argmixedEvery example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.
Fire your own action so other code can react, exactly the way core actions work.
// Anything can hook this, exactly like a core action.
add_action( 'wppaste_order_processed', function ( $order_id ) {
echo "listener: order {$order_id} was processed\n";
}, 10, 1 );
function wppaste_process_order( $order_id ) {
update_post_meta( $order_id, '_processed_at', time() );
do_action( 'wppaste_order_processed', $order_id );
}
wppaste_process_order( 2 );
echo 'the action has run ', did_action( 'wppaste_order_processed' ), ' time(s)';Callbacks must be added before do_action() runs; anything registered later never fires.
Pass several values after the hook name and have listeners opt in to them with $accepted_args.
$imported_count = 12;
$skipped_count = 3;
do_action( 'myplugin_import_finished', $imported_count, $skipped_count );
// Elsewhere, another plugin listens for it.
add_action(
'myplugin_import_finished',
function ( $imported, $skipped ) {
error_log( "Import done: {$imported} imported, {$skipped} skipped." );
},
10,
2
);Listeners only receive the extra arguments when their add_action() call sets $accepted_args to match; with the default of 1 the second value never arrives.
function do_action( $hook_name, ...$arg ) { global $wp_filter, $wp_actions, $wp_current_filter; if ( ! isset( $wp_actions[ $hook_name ] ) ) { $wp_actions[ $hook_name ] = 1; } else { ++$wp_actions[ $hook_name ]; } // Do 'all' actions first. if ( isset( $wp_filter['all'] ) ) { $wp_current_filter[] = $hook_name; $all_args = func_get_args(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection _wp_call_all_hook( $all_args ); } if ( ! isset( $wp_filter[ $hook_name ] ) ) { if ( isset( $wp_filter['all'] ) ) { array_pop( $wp_current_filter ); } return; } if ( ! isset( $wp_filter['all'] ) ) { $wp_current_filter[] = $hook_name; } if ( empty( $arg ) ) { $arg[] = ''; } elseif ( is_array( $arg[0] ) && 1 === count( $arg[0] ) && isset( $arg[0][0] ) && is_object( $arg[0][0] ) ) { // Backward compatibility for PHP4-style passing of `array( &$this )` as action `$arg`. $arg[0] = $arg[0][0]; } $wp_filter[ $hook_name ]->do_action( $arg ); array_pop( $wp_current_filter );}Introduced in 1.2.0. Unchanged from 6.7.7 through 7.1.0.
Signature, return type and hooks compared across 5 parsed releases.
src/wp-includes/plugin.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.