0

I'd like themes to be able to alter a default array of data inside a plugin class. It's currently set up like:

namespace Example;

class Example_Class {

    private $stuff;

    public function __construct() {

        $this->stuff = $this->set_stuff();

    }

    public function set_stuff() {

        $things = array(
            'first' => 'First',
            'second' => 'Second',
        );

        return apply_filters( 'my_cool_filter', $things );
     }

}

Then to test it I put this in the theme's functions.php:

function change_things( $things ) {

    $things = array(
        'third' => 'Third',
        'fourth' => 'Fourth',
    );

    return $things;
 }

 add_filter( 'my_cool_filter', 'change_things' );

However it's not working. The $stuff property is still getting set as the original default array, so add_filter() isn't having any effect. It would seem to be the same question as this, but I can't see what I'm doing wrong.

EDIT: The class is in a plugin and instantiated as part of the plugin loading - called from the main plugin file. The methods in the class manage settings and the main one that uses the property is hooked to admin_init, but if I add die( var_dump( $this->stuff ) ) inside the constructor just after it's set, I get the original array instead of the filtered array. I think it's clear from what everyone is saying that it must be an issue of things happening in the wrong order, but don't plugins always load before the theme?

4
  • Are you sure the array isn't being filtered/changed? Because your code seems just fine. So where/how do you call the class (new Example\Example_Class)?
    – Sally CJ
    Commented Nov 12, 2020 at 15:09
  • Maybe you call your class function before you add_filter
    – ZecKa
    Commented Nov 12, 2020 at 15:50
  • It is not the same question as the question you linked to, and apply_filters is being used correctly. However, we have no information about how your class is being created, when it's being created, where it's being created, or how you tested the stuff variable, can you edit your question and include that information?
    – Tom J Nowell
    Commented Nov 12, 2020 at 17:23
  • Thanks all - good to know at least the usage is correct. I've added more info - it seems that the do_action must be happening before the apply_filters but I'm not sure how to check that. Would it throw an error if I enable wp_debug?
    – Shoelaced
    Commented Nov 12, 2020 at 22:25

1 Answer 1

0

Your code is good — you are using both apply_filters() and add_filter() correctly.

But the timing/hook does not seem correct.

I think it's clear from what everyone is saying that it must be an issue of things happening in the wrong order, but don't plugins always load before the theme?

  • Wrong order: Yes — you most likely called apply_filters() before the filter (change_things()) is added (via add_filter()) or that you instantiated the class Example_Class in the wrong place/hook (i.e. instantiated too early).

  • Don't plugins always load before the theme? Yes they do, therefore you need to ensure your filter is added on-time — and the earliest hook you'd use to ensure your code runs after the theme is loaded would be after_setup_theme:

    /**
     * Fires after the theme is loaded.
     *
     * @since 3.0.0
     */
    do_action( 'after_setup_theme' );
    

    See this Codex article for a list of the hooks and their order of execution on a WordPress page, both admin and non-admin sides, but for an up-to-date list, try Query Monitor or the other options here.

So for example, if you instantiated the class in plugins_loaded, then the property Example_Class::$stuff would not be filtered by the change_things().

And here are two examples demonstrating how should and should not the class be instantiated:

  • On-time/good instantiation — admin_init runs after after_setup_theme:

    // In main plugin file:
    // Note: I used closure merely for testing purposes.
    
    add_action( 'admin_init', function () {
        require_once '/path/to/class-example-class.php';
        new Example\Example_Class;
        // the $this->stuff in the class would now be the $things returned by
        // change_things()
    } );
    

    And as you may have guessed it, you can use after_setup_theme in place of the admin_init. But in most plugins, they initialize things via the init hook which runs after WordPress setups things like the theme and current user:

    /**
     * Fires after WordPress has finished loading but before any headers are sent.
     *
     * Most of WP is loaded at this stage, and the user is authenticated. WP continues
     * to load on the {@see 'init'} hook that follows (e.g. widgets), and many plugins instantiate
     * themselves on it for all sorts of reasons (e.g. they need a user, a taxonomy, etc.).
     *
     * If you wish to plug an action once WP is loaded, use the {@see 'wp_loaded'} hook below.
     *
     * @since 1.5.0
     */
    do_action( 'init' );
    
  • Too-early instantiation — plugins_loaded runs before after_setup_theme:

    // In main plugin file:
    
    add_action( 'plugins_loaded', function () {
        require_once '/path/to/class-example-class.php';
        new Example\Example_Class;
        // change_things() was not executed
    } );
    
3
  • Hm, okay thanks!! Good to know. I'll test it this week, I'll have to refactor things a bit.
    – Shoelaced
    Commented Nov 14, 2020 at 18:20
  • So I'm wondering, how was your test? What was the problem and how did you fix it?
    – Sally CJ
    Commented Jan 13, 2021 at 14:12
  • 1
    I did some brief tests but ended up changing things around. Thanks for your help though!
    – Shoelaced
    Commented Jan 14, 2021 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.