0

I'm using the Wordpress boilerplate to develop a plugin. At some point, I wanted to make dynamic functions with some external parameters when using $this->loader->add_action() so I used an anonymous function... but it doesn't work. So I resolved to bypass the loader and use the Wordpress standard add_action() and it works! But I know it's not good practice. How to pass an anonymous function through the loader? I'd like to know if any of you have a idea of how to implement this with the loader.

What I tried

$this->loader->add_action("views_edit-{$post_type}", $plugin_admin, function($views) use ($plugin_admin, $post_type) { 
                return $plugin_admin->some_function($views, $post_type); 
            });

The error I get

 Your PHP code changes were rolled back due to an error on line 959 of file wp-includes/plugin.php. Please fix and try saving again.

Uncaught Error: Object of class Closure could not be converted to string in wp-includes/plugin.php:959
Stack trace:
#0 wp-includes/class-wp-hook.php(74): _wp_filter_build_unique_id()
#1 wp-includes/plugin.php(121): WP_Hook->add_filter()
#2 wp-includes/plugin.php(399): add_filter()
#3 wp-content/plugins/myplugin/includes/class-myplugin-loader.php(124): add_action()
#4 wp-content/plugins/myplugin/includes/class-myplugin.php(266): MyPlugin_Loader->run()
#5 wp-content/plugins/myplugin/myplugin.php(76): MyPlugin->run()
#6 wp-content/plugins/myplugin/myplugin.php(79): run_myplugin()
#7 wp-settings.php(418): include_once('/home/...')
#8 wp-config.php(106): require_once('/home/...')
#9 wp-load.php(50): require_once('/home/...')
#10 wp-admin/admin.php(34): require_once('/home/...')
#11 wp-admin/plugin-editor.php(10): require_once('/home/...')
#12 {main}
  thrown

What I used instead by bypassing the loader (it works)

add_action("views_edit-{$post_type}", function($views) use ($plugin_admin, $post_type) {
            return $plugin_admin->some_function($views, $post_type); 
        });
3
  • 1
    unfortunately this boilerplate framework doesn't support closures - the 2nd parameter MUST be an object, and the 3rd parameter MUST be a string representing a publicly accessible method available on the object provided in the 2nd parameter. To use the boilerplate approach, the siimplest solution is create a dedicated class with a method you can use in place of your anonymous function. I suppose you could use an anonymous class (php.net/manual/en/language.oop5.anonymous.php) but this would complicate things. Just create a class to do your work, and hook that in. Why not?
    – Paul G.
    Commented Mar 20, 2022 at 13:09
  • Thank you for your fast reply. I didn't know the boilerplate doesn't support closures, thanks for the clarification. As for your suggestion, even if I created a dedicated class to replace the anonymous function, I don't see where I could pass arguments through the loader since their is no parameter where to pass them?
    – Tom
    Commented Mar 20, 2022 at 21:32
  • Do you mean I should create a class where I pass the arguments I want to pass, then call that class in the 2nd parameter of the loader (object) and then call a function name inside that class as a 3rd parameter of the loader (string)?
    – Tom
    Commented Mar 20, 2022 at 21:40

1 Answer 1

0

Thanks Paul, you led me to the solution.

What I did

In class-mypluging-admin.php, I added this:

/**
 * Additional arguments for this plugin.
 *
 * @since    1.0.0
 * @access   private
 * @var      array $args The current arguments of this plugin.
 */
private $args;

/**
 * Initialize the class and set its properties.
 *
 * @since    1.0.0
 * @param      string    $plugin_name       The name of this plugin.
 * @param      string    $version    The version of this plugin.
 * @param      array     $args  The additional arguments of this plugin.
 */
public function __construct( $plugin_name, $version, $args = null ) {

    $this->plugin_name = $plugin_name;
    $this->version = $version;
    $this->args = $args;

}

Then for the functions in class-mypluging-admin.php

function some_function( $views ) {
    
    $post_type = $this->args['post_type'];

...

}

In class-mypluging.php, I do this:

foreach($post_types as $post_type)
{
    $args = array(
        'post_type' => $post_type,
    );
    
    $plugin_admin = new MyPlugin_Admin( $this->get_plugin_name(), $this->get_version(), $args );    

$this->loader->add_action("views_edit-{$post_type}", $plugin_admin, 'some_function');   

}

And it works! Is that the correct way to do this?

2
  • So my solution is actually flawed. 😔 since the loader fire the actions afterwards, it only uses the last assignment of the $args. I can't make it work with loops where I change dynamically the value of the $args.
    – Tom
    Commented Mar 21, 2022 at 23:47
  • Ok so, in order to make the $args change dynamically inside the loop, I instantiate a new $plugin_admin inside the loop. I edited my anwer accordingy. It works but I have absolutely no clue if it's a clean way or not to do so. So I'd appreciate a confirmation. 🙂
    – Tom
    Commented Mar 22, 2022 at 1:24

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.