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.

Creating Your Own Hooks

WordPress Plugin Development Lesson 6 of 27 ~3 min read

Overview

The best plugins are extensible: by calling do_action() and apply_filters() at key points in your own code, you let other developers — and your own add-ons — change your plugin's behaviour without ever editing its files.

Why this matters

Imagine a client needs the books shortcode to show 12 books instead of your default 6. Without a hook, the only option is to edit your plugin, and the edit is lost at the next update. With a filter, they add three lines to their own code and your plugin stays untouched.

Offering a filter

<?php
function gdc_books_get_query_args( $atts ) {
    $args = array(
        'post_type'      => 'gdc_book',
        'posts_per_page' => (int) $atts['count'],
        'orderby'        => 'date',
    );

    /**
     * Filters the WP_Query arguments used by the [gdc_books] shortcode.
     *
     * @since 1.0.0
     *
     * @param array $args WP_Query arguments.
     * @param array $atts The shortcode attributes.
     */
    return apply_filters( 'gdc_books_query_args', $args, $atts );
}

Anyone can now change the query from their own code:

<?php
// In a theme's functions.php or another plugin.
add_filter( 'gdc_books_query_args', function ( $args ) {
    $args['orderby'] = 'title';
    $args['order']   = 'ASC';
    return $args;
} );

Offering actions

Actions mark moments. A pair of "before" and "after" actions around anything your plugin prints gives developers a place to add their own markup:

<?php
function gdc_books_render_card( WP_Post $book ) {
    ob_start();

    /**
     * Fires before a book card is rendered.
     *
     * @param WP_Post $book The book being displayed.
     */
    do_action( 'gdc_books_before_card', $book );
    ?>
    <article class="gdc-book-card">
        <h3><?php echo esc_html( get_the_title( $book ) ); ?></h3>
    </article>
    <?php
    /**
     * Fires after a book card is rendered.
     *
     * @param WP_Post $book The book being displayed.
     */
    do_action( 'gdc_books_after_card', $book );

    return ob_get_clean();
}

And an action after an important event lets others react to it:

<?php
// After a book's details are saved (lesson 16):
do_action( 'gdc_books_details_saved', $post_id, $isbn );

// Someone else can now sync the ISBN to an external catalogue:
add_action( 'gdc_books_details_saved', 'my_sync_isbn', 10, 2 );

Naming and documenting hooks

  • Prefix every hook name: gdc_books_query_args, never query_args.
  • Document each one with a DocBlock directly above it, as shown — that is how tools generate hook documentation.
  • Add @since with the version that introduced it.
  • Treat hooks as a public API: once released, do not rename or remove them, or you break other people's code.
  • Use apply_filters_deprecated() and do_action_deprecated() when you must retire one, so developers get a warning first.

Practice

  • Add the gdc_books_query_args filter to your shortcode and use it from a theme to change the order.
  • Wrap your card markup in gdc_books_before_card and gdc_books_after_card actions and use one to add a "New" badge.
  • Write a DocBlock for each new hook.
Create a free account to save which lessons you've finished. Save my progress