SponsoredHosinger Special Offer

WP+ WordPress Plugin Development

Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.

Action Hooks

WordPress Plugin Development Lesson 4 of 27 ~3 min read

Overview

An action hook is a named point in WordPress's execution where it calls every function registered with add_action() for that name — letting your plugin run code when a post is saved, a user logs in or a page is rendered.

The anatomy of add_action()

<?php
add_action(
    'publish_post',           // 1. The hook name.
    'gdc_books_notify_admin', // 2. Your callback.
    10,                       // 3. Priority: lower numbers run first. Default 10.
    2                         // 4. How many arguments your callback accepts. Default 1.
);

function gdc_books_notify_admin( $post_id, $post ) {
    wp_mail(
        get_option( 'admin_email' ),
        'New post published',
        sprintf( '"%s" was just published: %s', $post->post_title, get_permalink( $post_id ) )
    );
}

WordPress core contains do_action( 'publish_post', $post_id, $post ). When that line runs, it calls every callback registered for publish_post, passing the arguments along. Because you set the fourth parameter to 2, your function receives both.

Watch out: The single most common hook bug: your callback expects two arguments but you left the fourth parameter of add_action() at its default of 1. PHP then throws "Too few arguments". If your function signature has N parameters, pass N.

Priority

When several callbacks hook the same action, priority decides the order. Callbacks with the same priority run in the order they were added.

<?php
add_action( 'wp_footer', 'gdc_books_footer_last', 100 ); // Runs after most others.
add_action( 'wp_footer', 'gdc_books_footer_first', 1 ); // Runs before most others.

The actions plugins use most

HookFires whenTypical use
plugins_loadedEvery plugin is loadedCheck for other plugins, run upgrades
initWordPress is ready, before outputRegister post types, taxonomies, shortcodes, blocks
admin_menuBuilding the dashboard menuAdd admin pages
admin_initEvery admin page loadRegister settings, handle admin form posts
wp_enqueue_scriptsFront-end assets are being collectedLoad front-end CSS and JS
admin_enqueue_scriptsAdmin assets are being collectedLoad admin CSS and JS
save_postA post is savedSave meta box fields
rest_api_initThe REST API is startingRegister REST routes
template_redirectBefore a front-end template loadsRedirects and access control

Using class methods and closures

<?php
// A method on an object:
add_action( 'init', array( $plugin, 'register_post_types' ) );

// A static method:
add_action( 'init', array( 'GDC_Books_Setup', 'register' ) );

// A closure — fine for tiny callbacks, but it can never be removed later:
add_action( 'init', function () {
    add_post_type_support( 'page', 'excerpt' );
} );
Tip: Prefer named functions or methods for anything another plugin or a site owner might want to switch off. remove_action() needs the exact same callback, and a closure cannot be referred to a second time.

Removing and inspecting actions

<?php
// Remove a callback: same hook, same callback, same priority.
remove_action( 'wp_head', 'wp_generator' );

// Has it been added?
if ( has_action( 'init', 'gdc_books_register_post_type' ) ) { /* ... */ }

// Has the hook already fired? Useful before relying on something registered there.
if ( did_action( 'init' ) ) { /* ... */ }

Practice

  • Hook publish_post and write the post title to debug.log with error_log().
  • Register two wp_footer callbacks with priorities 1 and 100 and check the output order in the page source.
  • Deliberately set the argument count to 1 for a two-argument callback and read the error, then fix it.
Create a free account to save which lessons you've finished. Save my progress