WordPress Hooks and Filters Order Of Precedence
When building plugins and themes I often need to reference the WordPress order of precedence of hooks. This helps ensure various components are loaded only when needed and at the right time. The base list I reference is the old Codex Plugin API/Action Reference page. Its sister resource, the Codex Plugin API/Filter Reference is also useful.
The only problem I have with those resources is when I need to determine what will fire on the front-end, backend (admin only) , AJAX, and Cron. This is my cheat sheet to help sort out the first two (front-end v. admin processing).
WordPress Action Hooks
Stage 1 : Loading
Notes
widgets_init is fired by hooking the init action. It fires at priority 1.
Stage 2 : Processing
After wp_loaded is called, this is where the front end and admin processes diverge.
Starting Up A Plugin
I use PHP autoloading to eliminate require and include statements throughout the code. It requires a consistent directory structure and file naming convention. If you follow PHP best practices this should not be an issue. I also recommend naming your PHP files after the class they contain and keeping every class in its own file.
Here is an example from my latest side project that sets up the autoloader and loads up the bulk of the plugin code when the WordPress plugins_loaded hook is fired. It also exits the plugin early if a WordPress heartbeat comes in since this plugin, like 99% of those out there, has no need to listen to the heartbeat and load up all the overhead.
<?php /* Plugin Name: Geo Census Plugin URI: https://www.storelocatorplus.com/ Description: United States geography intelligence plugin. Author: Store Locator Plus Author URI: https://www.storelocatorplus.com License: GPL3 Tested up to: 4.8.2 Version: 0.1 ---> Change GEO_CENSUS_VERSION define as well. Text Domain: geo-census Domain Path: /languages/ */ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_POST[ 'action' ] ) && ( $_POST[ 'action' ] === 'heartbeat' ) ) { return; } /** * Autoload our classes. * * @param string $class_name */ function GeoCensus_autoload( $class_name ) { if ( strpos( $class_name , 'GeoCensus_' ) === false ) { return; } // Set submodule and file name. // preg_match( "/GeoCensus_([a-zA-Z]*)_/" , $class_name , $matches ); $file_name = GEO_CENSUS_DIR . 'include/module/' . ( isset( $matches[ 1 ] ) ? strtolower( $matches[ 1 ] ) . '/' : '' ) . $class_name . '.php'; // If the include/module/submodule/class.php file exists, load it. // if ( is_readable( $file_name ) ) { require_once( $file_name ); } } spl_autoload_register( 'GeoCensus_autoload' ); /** * Start this up when the plugins are loaded in WP. */ function GeoCensus_loader() { defined( 'GEO_CENSUS_VERSION' ) || define( 'GEO_CENSUS_VERSION' , '0.1' ); defined( 'GEO_CENSUS_FILE' ) || define( 'GEO_CENSUS_FILE' , __FILE__ ); defined( 'GEO_CENSUS_DIR' ) || define( 'GEO_CENSUS_DIR' , plugin_dir_path( __FILE__ ) ); require_once( 'include/base/GeoCensus_Object.php' ); $GLOBALS[ 'geo_census' ] = new GeoCensus_Plugin(); } add_action( 'plugins_loaded' , 'GeoCensus_loader' );
Loading Code As Needed
Rather than load up a pile of code into memory when a plugin loads, I prefer to break down my code into classes that are only loaded as needed. No need to load up all that admin settings page overhead if we are only rendering a shortcode for a user on the front-end.
I use plugins_loaded to fire up my main plugin class and let the construction of that class manage the logic of when to load up the other modules.
class GeoCensus_Plugin { /** * GeoCensusPlugin constructor. */ public function __construct() { $this->connect_frontend(); $this->add_hooks_and_filters(); $this->cron_spawn_bot_babies(); $this->ajax_spackle(); } ...
Front End Only Processing
The main plugin loads a class that runs several methods during construction, adding hooks to admin_menu to load up the admin class when on the back end, testing for Cron or AJAX calls, and checking is_admin() and loaded the front end if NOT running an admin page.
/** * Load the front-end only module. */ private function connect_frontend() { if ( is_admin() ) return; new GeoCensus_FrontEnd(); }
Admin Only Processing
The admin_menu Hook
I often use the admin_menu hook to load up classes that are only needed on admin pages. My typical structure is to have a controlling MyClass_Admin object which loads via admin_menu and within that add intelligence to the loading by relegating any code that does not have to run during the admin_menu hook into further subclasses such as MyClass_Admin_Init which is loaded up via a method attached to the ‘admin_init’ hook.
I use this method if I do not need to load up features earlier in the call stack (Stage 1 Loading) such as tweaking the admin_bar.
/** * WordPress Hooks and Filters */ private function add_hooks_and_filters() { add_action( 'admin_menu' , function () { new GeoCensus_Admin(); new GeoCensus_AdminorAjax(); } ); // This will fire after our plugin is activated. }
The is_admin() Test
This built-in WordPress function can also be used to check if admin pages are being loaded. I tend to leave this as a sanity check to ensure we are on an admin page within my objects. This avoids the overhead of calling and is_admin() test on every single page load, though I’ve not yet tested if this is more or less efficient than tying into the admin_menu hook.
I use this method if I need things to happen earlier in the call stack than admin_menu allows such as modifying the admin menu bar.
AJAX Only Processing
I often load this when the main plugin is loaded, which gets invoked via the plugins_loaded hook. I typically use a method in the class that is loaded to manage the plugin that does a simple AJAX test:
/** * Stuff we do when processing AJAX requests. */ private function ajax_spackle() { if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) return; new GeoCensus_AdminorAjax(); }
Cron Only Processing
Similar to AJAX, Cron can fire at any time. It too is managed via plugins_loaded with the main plugin class and has a check for DOING_CRON:
/** * Go forth and spawn our cron bot babies. */ private function cron_spawn_bot_babies() { if ( ! defined( 'DOING_CRON' ) || ! DOING_CRON ) return; $cron_mom = GeoCensus_Cron_Manager::get_instance(); $cron_mom->make_bot_babies(); }
REST API Process
The REST endpoints need to be configured early in the process. Using the rest_api_init hook is a good place to attach things — but recently I ran into an issue when doing cross-domain REST processing where the endpoints are not connected soon enough.
The REST API invokes rest_api_loaded() via the WP parse_request action hook.
The rest_api_loaded() function lives in rest-api.php. It looks for a query variable named ‘rest_route’ via $GLOBALS[ ‘wp’ ]->query_vars[‘rest_route’]. If set it defines REST_REQUEST as true AND invokes rest_get_server().
Inside the rest_get_server() it will check if the global $wp_rest_server is created, if not it will create it and in the process fire off the rest_api_init hook.
One of the most useful lists for WordPress hooks firing sequence as you made this great tabular distinction between backend and frontend. Thank you for the nice tutorial, gets a bookmark!
Thanks, This really helped.