web-dev-qa-db-fra.com

add_action référence une classe

Est-il possible de référencer une classe au lieu d'une fonction dans 'add_action'? Je n'arrive pas à comprendre. Voici juste un exemple de base de la fonction en question.

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

Alors oui, ça ne marche pas. J'ai aussi essayé:

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

Et:

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

Et aussi:

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

Y a-t-il un moyen de le faire sans avoir à créer une fonction distincte qui charge la classe? J'aimerais pouvoir simplement exécuter le constructeur de classes via add_action. C'est tout ce qui doit être chargé pour lancer le processus.

34
Matthew Ruddy

Non, vous ne pouvez pas "initialiser" ou instancier la classe par un crochet, pas directement. Certains codes supplémentaires sont toujours nécessaires (et ce n’est pas souhaitable de pouvoir le faire, car vous ouvrez une boîte de Pandore.

Voici une meilleure façon de le faire:

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

Bien sûr, on pourrait créer une classe d'interface pour la simplifier encore plus pour le cas général:

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

Notez qu'en tant qu'interface, vous ne pouvez pas créer directement un objet de ce type, mais vous pouvez créer une sous-classe en vous permettant de dire:

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

Ce qui ajouterait ensuite automatiquement tous les points d'accroche, il vous suffira de définir ce qui est exactement fait dans une sous-classe, puis de le créer!

Sur constructeurs

Je n’ajouterais pas de constructeur en tant que fonction de hook, c’est une mauvaise pratique et je peux mener beaucoup d’événements inhabituels. De plus, dans la plupart des langages, un constructeur renvoie l'objet en cours d'instanciation. Par conséquent, si votre hook doit renvoyer quelque chose comme dans un filtre, il ne renverra pas la variable filtrée comme vous le souhaitez, mais plutôt l'objet de classe.

Appeler un constructeur ou un destructeur est une très, très très mauvaise pratique de programmation, quelle que soit la langue dans laquelle vous vous trouvez et que vous devriez jamais faire.

Les constructeurs doivent également construire des objets, pour les initialiser, prêts à être utilisés et non pour le travail réel. Le travail à effectuer par l'objet doit être dans une fonction distincte.

Méthodes de classe statiques, et n'ayant pas du tout besoin d'instancier/initialiser

Si votre méthode de classe est une méthode de classe statique, vous pouvez indiquer le nom de la classe entre guillemets plutôt que $this, comme indiqué ci-dessous:

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

Fermetures & PHP 5.3

Malheureusement, vous ne pouvez pas éviter la ligne créant la nouvelle classe. La seule autre solution à éviter consiste à utiliser un code de plaque de chaudière contenant toujours cette ligne et nécessitant PHP 5.3+, par exemple:

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

À ce stade, vous pouvez également ignorer la classe et utiliser simplement une fonction:

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

Mais gardez à l'esprit que vous avez maintenant introduit le spectre des fonctions anonymes. Il n'y a aucun moyen de supprimer l'action ci-dessus à l'aide de remove_action, et cela peut causer des problèmes aux développeurs qui doivent travailler avec le code d'autres personnes.

Sur les esperluettes

Vous pouvez voir des actions utilisées comme ceci:

add_action( &$this, 'getStuffDone' );

C'est mauvais . & a été rajouté dans PHP 4 lorsque les objets étaient transmis en tant que valeurs et non en tant que références. PHP 4 a plus de dix ans et n'a pas été supporté par WordPress depuis très longtemps.

Il n'y a aucune raison d'utiliser &this lors de l'ajout de points d'ancrage et de filtres, et la suppression de la référence ne posera aucun problème et pourra même améliorer la compatibilité avec les futures versions de PHP.

Utilisez ceci à la place:

add_action( $this, 'getStuffDone' );
63
Tom J Nowell

Exemple de classe

Remarques:

  • Commencez la classe une seule fois
    • Appelez sur la priorité 0 pour pouvoir utiliser ultérieurement le même point d'ancrage avec la priorité par défaut
    • Enveloppez-le dans un ! class_exists pour éviter de l'appeler deux fois et placez l'appelant init à l'intérieur
  • Créez la fonction init et la classe var static
  • Appelez le constructeur de l'intérieur de votre init lorsque vous appelez la classe new self.

Voici un exemple

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+

S'il vous plaît , laissez le &. Nous sommes déjà au-delà de php4. :)

10
kaiser
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();
}
2
Zakir Sajib

Vous pouvez déclencher des événements dans votre classe sans avoir besoin de le charger initialement . C'est pratique si vous ne voulez pas charger toute la classe à l'avance, mais que vous devez accéder aux filtres et actions WordPress.

Voici un exemple très simple

<?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();

Ceci est une version très simplifiée de la réponse de Kaiser mais montre en termes simples le comportement. Pourrait être utile pour ceux qui regardent ce style pour la première fois.

D'autres méthodes peuvent toujours initier l'objet si nécessaire. Personnellement, j'utilise cette méthode pour autoriser des parties facultatives de mon plug-in à mettre en file d'attente des scripts, mais uniquement en déclenchant l'objet sur une requête AJAX.

0
John Reid

De manière générale, vous ne voudriez pas ajouter un class entier à un hook. Les hooks add_action()/add_filter() s'attendent à un rappel functions , auquel peut être référencé depuis au sein d'une classe .

Supposons que vous ayez une fonction init() dans votre classe et que vous souhaitez vous connecter au hook WordPress init.

Mettez votre add_action() appel inside votre classe, puis identifiez le rappel comme suit:

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

(Remarque: je suppose que votre classe utilise correctement les noms, sinon, assurez-vous de nommer vos fonctions de rappel.)

0
Chip Bennett

Vous devriez pouvoir le faire en passant le nom de la classe à la place de l'objet instancié:

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

(En théorie, votre autre solution devrait aussi fonctionner

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

Je ne sais pas pourquoi. Peut-être que si vous n'appelez pas par référence?)

0
Boone Gorges

Cela fonctionne pour moi:

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

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