The add_action function is the main function to use when creating WordPress plugins. You mainly use it for executing code at various run time points within WordPress. With this function, you can create shortcodes, enqueue scripts, styles, modify post titles and contents and lots more.

How does this work? If you explode the core code of WordPress, it is littered with do_action calls all over. Every time WordPress does a do_action, all function passed to matching add_action calls get executed. This is very helpful in the creation of modular and maintainable code, and it’s great.

So if you perform 2 add_action calls to an action named wp_head, this means that when WordPress calls a do_action( 'wp_head' ), your 2 functions in your add_actions get called.

The Problem

In Titan Framework, we have a lot of add_action calls. But since we designed Titan Framework to be object oriented (OOP), some of our actions were getting called multiple times when we only intended for it to run once. How is that? Check this brief example of what a color option class might look like:

class TitanFrameworkColorOption {

    function __construct() {
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueueScripts' ) );
    }

    public function enqueueScripts() {
        wp_enqueue_script( 'wp-color-picker' );
        wp_enqueue_style( 'wp-color-picker' );
    }
}

This is a very stripped-to-the-basics class of the option (in reality it’s much more code than this). With the code above, when you create a color option TitanFrameworkColorOption, we enqueue the script and styles of WordPress’ built-in color picker.

The problem here is that when two color options are created via new TitanFrameworkColorOption(), that means that the add_action function is called twice, and therefore the enqueueScripts function is ran twice. This might not be much of a problem with the example code above since WordPress doesn’t enqueue the same thing multiple times. But if you have a more elaborate code in there, then those would get executed twice. That could lead to more memory and CPU usage for your server, and slower loading times.

The solution then is to create a checker so that we only run enqueueScripts once. Let’s add that in:

class TitanFrameworkColorOption {

    public static $alreadyEnqueued = false;

    function __construct() {
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueueScripts' ) );
    }

    public function enqueueScripts() {
        if ( ! self::$alreadyEnqueued ) {
            wp_enqueue_script( 'wp-color-picker' );
            wp_enqueue_style( 'wp-color-picker' );
        }
        self::$alreadyEnqueued = true;
    }
}

This now works fine. The bad side is that you now have added 4 new lines of code. If you have a large codebase, this can scale up fast and your code slowly becomes a little bit harder to read.

The Solution

/**
 * Performs an add_filter only once. Helpful for factory constructors where an action only
 * needs to be added once. Because of this, there will be no need to do a static variable that
 * will be set to true after the first run, ala $firstLoad
 *
 * @since 1.9
 *
 * @param string   $tag             The name of the filter to hook the $function_to_add callback to.
 * @param callback $function_to_add The callback to be run when the filter is applied.
 * @param int      $priority        Optional. Used to specify the order in which the functions
 *                                  associated with a particular action are executed. Default 10.
 *                                  Lower numbers correspond with earlier execution,
 *                                  and functions with the same priority are executed
 *                                  in the order in which they were added to the action.
 * @param int      $accepted_args   Optional. The number of arguments the function accepts. Default 1.
 *
 * @return true
 */
function add_filter_once( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
	global $_gambitFiltersRan;

	if ( ! isset( $_gambitFiltersRan ) ) {
		$_gambitFiltersRan = array();
	}

	// Since references to $this produces a unique id, just use the class for identification purposes
	$idxFunc = $function_to_add;
	if ( is_array( $function_to_add ) ) {
		$idxFunc[0] = get_class( $function_to_add[0] );
	}
	$idx = _wp_filter_build_unique_id( $tag, $idxFunc, $priority );

	if ( ! in_array( $idx, $_gambitFiltersRan ) ) {
		add_filter( $tag, $function_to_add, $priority, $accepted_args );
	}

	$_gambitFiltersRan[] = $idx;

	return true;
}

The solution is this new function called add_filter_once. This runs similar to add_action except that it only executes the function passed to it once, even if it is called multiple times.

If we implement this to our class, we end up with a much more elegant code that’s still very much readable:

class TitanFrameworkColorOption {

    function __construct() {
        add_action_once( 'admin_enqueue_scripts', array( $this, 'enqueueScripts' ) );
    }

    public function enqueueScripts() {
        wp_enqueue_script( 'wp-color-picker' );
        wp_enqueue_style( 'wp-color-picker' );
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*