add_action référence une classe


38

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.

Réponses:


69

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 les 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 même, 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 ne devrait jamais être utilisé.

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 passer le nom de la classe entre guillemets plutôt que $thiscomme indiqué ci-dessous:

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

Fermetures et 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 en utilisant remove_action, et cela peut causer des problèmes aux développeurs qui doivent travailler avec du code d'autrui.

Sur les esperluettes

Vous pouvez voir des actions utilisées comme ceci:

array( &$this, 'getStuffDone' );

C'est mauvais . &a été rajouté en PHP 4 lorsque les objets étaient passés 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 &thislors de l'ajout de points d'ancrage et de filtres, et la suppression de la référence ne posera aucun problème et pourrait même améliorer la compatibilité avec les futures versions de PHP.

Utilisez ceci à la place:

array( $this, 'getStuffDone' );

D'accord. Merci de tout cela; vraiment appris un peu. Je ne suis vraiment à l'aise qu'avec le langage PHP basé sur les cours maintenant. C’est ce que j’ai fait et cela fonctionne, mais pourriez-vous me dire si c’est une mauvaise pratique / une erreur? J'ai initié la classe dans une fonction statique, dans la classe elle-même. Puis référencé la fonction statique dans add_action. Voir ce lien: pastebin.com/0idyPwwY
Matthew Ruddy

oui, vous pourriez le faire de cette façon, même si je pourrais éviter d'utiliser $classcomme nom de variable, ces mots ont tendance à être réservés. Je pense que vous vous efforcez d'éviter de dire quelque chose de similaire à $x = new Y();la portée mondiale, et vous ajoutez de la complexité là où aucune n'est nécessaire. Votre tentative de réduire la quantité de code écrite a impliqué d’écrire plus de code!
Tom J Nowell

Dans tous les cas ci-dessus, il serait préférable d'utiliser une fonction plutôt que une classe, car cette classe sera supprimée de toute façon et servira le même but. C'est un gaspillage de ressources. Rappelez-vous que la création et la destruction d'un objet ont un coût, vous voulez peu d'objets et vous voulez qu'ils vivent longtemps
Tom J Nowell

Bon point. Changé la façon dont je l'ai regardé. Je pense que je vais plutôt appeler cela dans la portée globale, en évitant le code supplémentaire.
Matthew Ruddy

1
Je voudrais ajouter que si vous avez mis votre classe dans un espace de noms, vous devrez également ajouter cela ou add_action () / add_filter () ne le trouvera pas - comme ceci:add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
jschrab

10

Exemple de classe

Remarques:

  • Commencez la classe une seule fois
    • Appelez sur la priorité 0 pour pouvoir utiliser ultérieurement le même hook avec la priorité par défaut
    • Enveloppez-le dans un ! class_existspour éviter de l'appeler deux fois et placez l'appelant init à l'intérieur
  • Faire la initfonction et la classe varstatic
  • 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 &dehors. Nous sommes déjà au-delà de php4. :)


2
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();
}

Veuillez modifier votre réponse et ajouter une explication: pourquoi cela pourrait-il résoudre le problème?
fuxia

0

Vous pouvez déclencher des événements dans votre classe sans avoir à 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

Cela fonctionne pour moi:

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

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

0

De manière générale, vous n’ajoutez pas une classe entière à un crochet. Les add_action()/ add_filter()hooks attendent des fonctions de rappel , qui peuvent être référencées depuis une classe .

Disons que vous avez une init()fonction dans votre classe, que vous voulez accrocher au hook WordPress init.

Mettez votre add_action()appel dans 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.)


Qu'en est-il si la classe actuelle n'a pas déjà été lancée? J'essayais d'utiliser add_action pour lancer réellement, je n'ai donc pas besoin d'ajouter $ var = new MyClass (); auparavant ailleurs.
Matthew Ruddy

Ne pouvez-vous pas simplement écrire un rappel pour instancier votre classe à init(ou partout où vous en avez besoin)?
Chip Bennett

0

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


Le premier ne fonctionne pas. Fait juste que la page devienne blanche. La seconde fonctionne, mais uniquement parce que la classe a été lancée à la première ligne. Cela annule en quelque sorte le but de l'ajout de l'action, car la classe a déjà été lancée. Mais cela ne fait pas ce que j'essaye de faire, c'est-à-dire initier la classe par l'action. Dans le code sur lequel je travaille, l'action n'est pas 'admin_init' mais une action personnalisée dans une autre classe. Je ne veux pas que la fonction 'MyClass' soit lancée si l'action dans l'autre classe n'y est pas. Désolé si je manque quelque chose; apprendre comme je vais
Matthew Ruddy

Oui, j'avais tort. Cela ne fonctionne que si vous appelez une initméthode statique . Voir wordpress.stackexchange.com/a/48093/14052
Gorges de Boone
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.