TransWikia.com

add_action reference a class

WordPress Development Asked by Matthew Ruddy on December 26, 2021

Is it possible to reference a class instead of a function in ‘add_action’? Can’t seem to figure it out. Here is just a basic example of the function in question.

add_action( 'admin_init', 'MyClass' );
class MyClass {
     function __construct() {
          .. This is where stuff gets done ..
     }
}

So yeah, that doesn’t work. I’ve also tried:

$var = new MyClass();
add_action( 'admin_init', array( &$var ) );

And:

$var = new MyClass();
add_action( 'admin_init', array( &$var, '__construct' ) );

And also:

add_action( 'admin_init', 'MyClass::__construct' );

Is there anyway I can do this without having to create a separate function that loads the class? I’d like to be able to just run the classes constructor through the add_action. That’s all that needs to be loaded to get the ball rolling.

7 Answers

if (!class_exists("AllInOneWoo")){
    class AllInOneWoo {
        function __construct(){
            add_action('admin_menu', array($this, 'all_in_one_woo') );
        }
        function all_in_one_woo(){
            $page_title = 'All In One Woo';
            $menu_title = 'All In One Woo';
            $capability = 'manage_options';
            $menu_slug  = 'all-in-one-woo-menu';
            $function   = array($this, 'all_in_one_woo_menu');
            $icon_url   = 'dashicons-media-code';
            $position   = 59;

            add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position);
        }
        function all_in_one_woo_menu(){?>
            <div class="wrap">
                <h1><?php _e('All In One Woo', 'all_in_one_woo'); ?></h1>
            </div>
        <?php }
    }// end class
}// end if

if (class_exists("AllInOneWoo")){       
    $all_in_one_woo = new AllInOneWoo();
}

Answered by Zakir Sajib on December 26, 2021

This works for me:

class foo
{
    public static function bar()
    {
        echo 'it works!';
    }
}

add_action('foo_bar', array('foo', 'bar'));

Answered by Jonathan on December 26, 2021

You can trigger events in your class without the need to load it initially. This is handy if you don't want to load the full class up front, but need to access the WordPress filters and actions.

Here's a very simple example

<?php
class MyClass
{
    public static function init()
    {
        add_filter( 'the_content', array('MyClass', 'myMethod') );
    }

    public static function myMethod($content)
    {
        $content = $content . 'Working without loading the object';
    }
}

MyClass::init();

This is a very simplified version of Kaiser's answer but shows in simple terms the behaviour. Might be handy for those looking at this style for the first time.

Other methods can still initiate the object if required. I'm personally using this method to allow optional parts of my plugin to enqueue scripts, but only triggering the object on an AJAX request.

Answered by John Reid on December 26, 2021

No, you cannot 'initialise' or instantiate the class through a hook, not directly. Some additional code is always required ( and it is not a desirable thing to be able to do that, as you're opening a can of worms for yourself.

Here is a better way of doing it:

class MyClass {
     function __construct() {
          add_action( 'admin_init', [ $this, 'getStuffDone' ] );
     }
     function getStuffDone() {
          // .. This is where stuff gets done ..
     }
}
$var = new MyClass();

Of course one could create an interface class to simplify it for the general case even further:

class IGetStuffDone {
    function IGetStuffDone(){
        add_action( 'admin_init', [ $this, 'getStuffDone' ] );
    }
    public abstract function getStuffDone();
}

Note that as an interface, you can't create an object of this type directly, but you could create a sub-class, letting you say:

class CDoingThings extends IGetStuffDone {
    function getStuffDone(){
        // doing things
    }
}
$var = new CDoingThings();

Which would then automatically add all the hooks, you just need to define what exactly is being done in a subclass and then create it!

On Constructors

I wouldn't add a constructor as a hook function, it's bad practice, and can lead ot a lot of unusual events. Also in most languages a constructor returns the object that is being instantiated, so if your hook needs to return something like in a filter, it will not return the filtered variable as you want, but instead it will return the class object.

Directly calling a constructor or a destructor is very, very, very bad programming practice, no matter which language you're in, and should never be done.

Constructors should also construct objects, to initialise them ready for use, not for actual work. Work to be done by the object should be in a separate function.

Static class methods, and not needing to instantiate/initialise at all

If your class method is a static class method, you can pass the name of the class in quotes rather than $this as shown below:

class MyClass {
    public static function getStuffDone() {
        // .. This is where stuff gets done ..
    }
}
add_action( 'admin_init', [ __NAMESPACE__ . 'MyClass','getStuffDone' ] );

Note the use of __NAMESPACE__ which is required if your class is inside a namespace.

Closures

Sadly you cannot avoid the line creating the new class. The only other solution to skipping it would involve boiler plate code that still has that line e.g.:

add_action( 'admin_init', function() {
    $var = new MyClass();
    $var->getStuffDone();
} );

At which point you may as well skip the class, and just use a function:

add_action( 'admin_init', function() {
   // do stuff
} );

But keep in mind you have now introduced the spectre of anonymous functions. There is no way to remove the above action using remove_action, and this can and does cause great pain for developers who have to work with other peoples code.

On Ampersands

You may see actions used like this:

array( &$this, 'getStuffDone' )

This is bad. & was added back in PHP 4 when objects were passed as values, not as references. PHP 4 is more than a decade old, and hasn't been supported by WordPress in a very long time.

There is no reason to use &this when adding hooks and filters, and removing the reference will cause no issues, and may even improve compatibility with future versions of PHP

Use this instead:

[ $this, 'getStuffDone' ]

Answered by Tom J Nowell on December 26, 2021

Example class

Notes:

  • Init the class only once
    • Call on priority 0, so you can use the same hook with the default priority later
    • Wrap it up in a ! class_exists to avoid calling it twice and place the init caller inside
  • Make the init function and the class var static
  • Call the constructor from inside your init, when you call the class new self.

Here's an example

if ( ! class_exists( 'WPSESampleClass' ) )
{
    // Init the class on priority 0 to avoid adding priority inside the class as default = 10
    add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );

class WPSESampleClass
{
    /**
     * The Class Object
     */
    static private $class = null;

    public static function init()
    {
        if ( null === self::$class ) 
            self :: $class = new self;

        return self :: $class;
    }

    public function __construct()
    {
        // do stuff like add action calls:
        add_action( 'init', array( $this, 'cb_fn_name' ) );
    }

    public function cb_fn_name()
    {
        // do stuff 
    }
} // END Class WPSESampleClass

} // endif;

Php 5+

Please, leave the & out. We're already beyond php4. :)

Answered by kaiser on December 26, 2021

You should be able to do it by passing the class name instead of the instantiated object:

add_action( 'init', array( 'MyClass', '__construct' ) );

(In theory, your other solution should work too

$var = new MyClass();
add_action( 'admin_init', array( $var, '__construct' ) );

Not sure off the head why it doesn't. Maybe if you don't call by reference?)

Answered by Boone Gorges on December 26, 2021

Generally speaking, you wouldn't add an entire class to a hook. The add_action()/add_filter() hooks expect callback functions, which can be referenced from within a class.

Let's say that you have an init() function inside your class, that you want to hook into the WordPress init hook.

Put your add_action() call inside your class, and then identify the callback like so:

add_action( 'init', array( $this, 'init' ) );

(Note: I'm assuming your class is properly namespaced; otherwise, be sure to namespace your callback functions.)

Answered by Chip Bennett on December 26, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP