skip to Main Content

I’m facing an issue that I can’t seem to figure out.

I have a plugin where it has filters that lets you change modify some of its data.

I have a simply class method like this inside the plugin:

public static function getAllowedColors(){
  $colors = array(
    'green',
    'blue',
  );
 return apply_filters('my_allowed_colors', $colors);
}

This method is used throughout the plugin like this Colors::getAllowedColors() but it should be filterable so third party plugins or devs can change it.

The issue is that I created and activated a small custom plugin, that doesn’t have any classes or anything..just the main plugin file with the required plugin headers and in that file, I did the usual:

function add_allowed_color($colors){
 $colors[] = 'black';
 return $colors;
}
add_filter('my_allowed_colors', 'add_allowed_color');

For some reason the new color never gets added when getAllowedColors() is called…

I added a dump statement inside the getAllowedColors() method, and then a dump statement inside the small custom plugin main plugin file (where the add_filter exists) and I can see that getAllowedColors is being called before the small custom plugin.

I’m aware that add_filter needs to be fired before apply_filters so that your changes can take effect…but I’m not sure how exactly to make sure that my little custom plugin always runs BEFORE the main plugin with all its classes and functionality.

I stumbled across this answer for a similar issue: https://stackoverflow.com/a/19279650/4484799 but it wouldn’t be applicable in this case since this getAllowedColors() is a utility method that gets called when needed throughout the plugin.


In the small custom plugin I also tried adjusting the code to this:

add_action('after_setup_theme', function(){

    function add_allowed_color($colors){
        $colors[] = 'black';
        return $colors;
    }

    add_filter('my_allowed_colors', 'add_allowed_color');
});

Though this is valid PHP, and if I called add_allowed_color('test') at the bottom of its definition, it returns test, the filter however still doesn’t work.

I want to know how I can make use of this filter? What is preventing it from working? I’ve used add_filter several times before between several different plugins especially WooCommerce which has a ton of filters but I can’t figure out why this isn’t working

Any help is appreciated

2

Answers


  1. @ChrisHaas has it right. In your small custom plugin, add your filter from a ‘plugins_loaded’ action handler instead of the one you used. ‘plugins_loaded’ doesn’t fire until everything is initialized with all the plugins in use.

    Login or Signup to reply.
  2. I’m going to start by saying something you already know: WordPress is extended by hooks.

    As I said, I know you know this, but I’d further clarify it by saying: WordPress should only be extended by hooks.

    What I mean by that is that any code not run in a hook should also make no assumptions about the state of WordPress.

    Based on comments by the OP, they have a singleton or controller or similar that boots up when their plugin entry file is executed. This is a very common pattern for plugins, and by itself isn’t necessarily right or wrong as long as this code only does PHP things such as instantiating classes, registering autoloaders or creating global functions, or registers WordPress actions/filters. If this code does anything else specific to WordPress, that’s where trouble can creep in.

    The reason for the trouble is that you have no guarantee that other plugins have been loaded, and it is absolutely certain that the theme hasn’t been either. And because these haven’t been loaded, they haven’t had a chance to register their filters.

    Even something as simple as get_option has at least six hooks that could be called in its lifecycle, and the simple translation function __ has two. Also, "simple" functions such as get_home_url still call get_option behind the scenes. Using any of these functions outside of a hook could lead to unexpected results.

    As a developer it really helps to understand the load order, at least a high level:

    1. WordPress begins the general load
    2. Dropins are loaded
    3. More WordPress loading including database
    4. Must-use plugins are loaded
    5. Active plugins are loaded
    6. The theme is loaded
    7. The user is authenticated

    For each of the last four in that list, once that specific step is completed there is also a corresponding hook:

    1. muplugins_loaded
    2. plugins_loaded
    3. after_setup_theme
    4. init

    If you run code that interacts with WordPress in any way (beyond filters) before the corresponding hook runs, you should assume other code at that level hasn’t run. For instance, if you are writing a plugin that executes code on entry, that means plugins_loaded, after_setup_theme and init haven’t run, and it is totally possible that other plugins and themes haven’t registered any hooks. Similarly, if your plugin runs code in the plugins_loaded loaded hook, then it should be assumed that the theme hasn’t been booted yet so it hasn’t registered any hooks.

    Sidenote: Plugins should not register the muplugins_loaded hook, and themes should not register the plugins_loaded hook because by the time registration happens, the hook has already run. I’ve definitely seen this happen several times in developer’s code.

    tl;dr

    Unless you need something more specific, the vast majority of code in a plugin can be invoked via the init hook because at this point the bulk of WordPress has been loaded.

    Further, there’s usually no advantage of creating your singleton "sooner than later". However, if your singleton is the one that does all the additional hook registration (another common pattern I see), it might be worth moving that code so that the singleton can be used for its instance methods only by the code that needs it.

    Edit

    I’ll amend this by saying that sometimes things appear to work depending on the plugin load order which is generally alphabetical.

    If you have plugins A and B, and plugin A happens to load before B, then it will be able to register its hooks before B needs them.

    This should only be considered working by coincidence however. The plugin load order can actually be changed, or you or the other plugin author might rename your plugin some day.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search