WordPress Plugin Loader Tricks

An unusually short article, for me, on basic WordPress plugin loader tricks.

Basic setup

Name the “loader” php file the same as the plugin directory.

Text Domain must match the directory name.

Avoid leading * on header lines = less bytes to process by the header processor in WordPress.

Ensure it runs from within WordPress

Use function_exists( ‘add_action’ ) instead of defined( ‘ABSPATH’).  It is more likely to be specific to WordPress.   It is also a better test as the loader calls the add_action function and will break if it is AWOL.

Not doing heartbeat processing

Short circuit to avoid loading the main plugin code if your plugin has zero influence on heartbeat operations.    WordPress heartbeats fire up AJAX requests every minute or two when a user is on the site.

Load on plugins_loaded

The plugins_loaded action loads early enough to be able to latch onto actions that have to happen in init or admin_menu. If your plugin requires others to be loaded this ensures that has happened, just make sure you set the action priority higher than the plugin you rely.
Read More

Automated Web App Testing With phpStorm

Selenium IDE was a great way to handle automated web app testing like the Store Locator Plus plugins for WordPress.    Selenium IDE is a simple script recorder and playback too that runs on Firefox.    Or, I should say, it used to run on Firefox.  That broke in 2017 when Firefox 52 came out.

After a lot of research I finally found a viable alternative to Selenium IDE that will work with modern browsers.  It is also free, locally installed, and open source. All winning attributes.  Paying for services is not much of an issue so the free part is not a requirement just a “that’s nice” feature.

Web app testing services

I tried several paid alternatives including Browserstack — a paid monthly service that runs virtual desktops and mobile device simulations hosting various browsers. Having to connect to a remote server via proxies or tunnels is a pain.    It also means no testing when offline or when the network is unreliable.    Having multiple browsers is great but 90% of the testing that needs to happen is base functionality which is the same across browsers.    Modern browser are also very good at testing mobile with browser like Safari going beyond simple screen sizing in their mimic of IOS, for example.

Other alternatives included several locally installed proprietary test frameworks.   Nearly every one of them ranges from mediocre to downright horrid.    This is clearly an industry stuck in the 1990s mindset of application development — from the start where you have to fill out a form with all your contact info to be “allowed” to demo the product (and be later harassed by sales people) to the 1980s desktop-centric interfaces.    Many did not work on MacOS.   Those that worked were heavy, bloated, and had a steep learning curve.    Does nobody integrate with my phpStorm, my web app IDE?

It just so happens that the best local testing suite today happens to be free.

The winner?   Selenium Webdriver with a few libraries like WebDriverIO + Mocha + Chai to make things easy.
Read More

WordPress wp_update_plugins Deep Dive

When using a private update service for premium WordPress plugins, some version tests get left behind leaving users with a half-updated plugin stack.   When operating a freemium model, like Store Locator Plus, where the free base plugin may be updated and impact how the premium add ons work having all update notifications arrive at once is critical.     There are times when version 3 of the main plugin will ONLY work with version 2 of a premium add on.    Within Store Locator Plus there are “pre-flight” checks built into both the base plugin and the premium add ons to ensure the entire WordPress site stays running if there is a conflict.
Read More

Using Custom Vagrant Sites For WordPress Development

As with many things in life, I recently learned that I’ve been doing things the hard way when it comes to WordPress development and Vagrant.

I got on the Varying-Vagrant-Vagrants bandwagon years ago.   It made it easy to write my Store Locator Plus code and test it on several different WordPress installations.    Along the way I learned how to provision my test boxes and share code between them.   When VVV 2 came out earlier this year I decided to upgrade.  It did make things easy, but in the early days of limited documentation I followed a few sparse examples and “figure out my own way”.   Of course that turned out to be the hard way of doing things.    I’m going to share with you the shortcuts I learned today to hopefully save you some time.


Read More

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

Front EndAdmin Pages
muplugins_loadedmuplugins_loaded
registered_taxonomyregistered_taxonomy
registered_post_typeregistered_post_type
plugins_loadedplugins_loaded
sanitize_comment_cookiessanitize_comment_cookies
setup_themesetup_theme
load_textdomainload_textdomain
after_setup_themeafter_setup_theme
auth_cookie_malformed
auth_cookie_validauth_cookie_valid
set_current_userset_current_user
initinit
widgets_init *widgets_init *
register_sidebarregister_sidebar
wp_register_sidebar_widgetwp_register_sidebar_widget
wp_default_scripts wp_default_scripts
wp_default_styles wp_default_styles
admin_bar_init admin_bar_init
add_admin_bar_menus add_admin_bar_menus
wp_loaded wp_loaded

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.

Front EndAdmin Pages
parse_request auth_redirect
send_headers _admin_menu
parse_query  admin_menu
pre_get_posts  admin_init
posts_selection current_screen
load-(page)
send_headers
pre_get_posts
posts_selection
wp  wp
template_redirect 
get_header
wp_enqueue_scripts admin_enqueue_scripts
wp_head
 admin_print_styles-(hookname)
 admin_print_styles
 admin_print_scripts-(hookname)
 admin_print_scripts
wp_print_scripts  wp_print_scripts
 admin_head-(hookname)
 admin_head
 adminmenu
in_admin_header
get_search_form admin_notices
all_admin_notices
loop_start restrict_manage_posts
the_post the_post
get_template_part_content  pre_user_query
loop_end 
get_sidebar 
dynamic_sidebar 
get_search_form 
pre_get_comments 
wp_meta 
get_footer   in_admin_footer
get_sidebar 
wp_footer  admin_footer
wp_print_footer_scripts 
admin_bar_menu  admin_bar_menu
wp_before_admin_bar_render wp_before_admin_bar_render
wp_after_admin_bar_render wp_after_admin_bar_render
 admin_print_footer_scripts
 admin_footer-(hookname)
shutdown shutdown
wp_dashboard_setup

 

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.

 

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.

 

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.

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.

 

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:

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:

 

 

%d bloggers like this: