add_action verweist auf eine Klasse


38

Ist es möglich, eine Klasse anstelle einer Funktion in 'add_action' zu referenzieren? Kann nicht scheinen, es herauszufinden. Hier ist nur ein einfaches Beispiel für die betreffende Funktion.

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

Also ja, das geht nicht. Ich habe auch versucht:

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

Und:

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

Und auch:

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

Kann ich das trotzdem tun, ohne eine separate Funktion erstellen zu müssen, die die Klasse lädt? Ich möchte in der Lage sein, den Klassenkonstruktor durch die add_action gerade laufen zu lassen. Das ist alles, was geladen werden muss, um den Ball ins Rollen zu bringen.

Antworten:


69

Nein, Sie können die Klasse nicht direkt über einen Hook 'initialisieren' oder instanziieren. Es wird immer zusätzlicher Code benötigt (und es ist nicht wünschenswert, dies zu tun, da Sie selbst eine Dose Würmer öffnen.

Hier ist eine bessere Möglichkeit:

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

Natürlich könnte man eine Interface-Klasse erstellen, um sie für den allgemeinen Fall noch weiter zu vereinfachen:

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

Beachten Sie, dass Sie als Schnittstelle ein Objekt dieses Typs nicht direkt erstellen können, sondern eine Unterklasse mit den folgenden Worten erstellen können:

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

Um dann automatisch alle Haken hinzuzufügen, müssen Sie nur definieren, was genau in einer Unterklasse gemacht wird, und es dann erstellen!

Auf Konstruktoren

Ich würde keinen Konstruktor als Hook-Funktion hinzufügen, das ist eine schlechte Praxis und kann zu vielen ungewöhnlichen Ereignissen führen. Auch in den meisten Sprachen gibt ein Konstruktor das Objekt zurück, das instanziiert wird. Wenn Ihr Hook also etwas wie in einem Filter zurückgeben muss, gibt er die gefilterte Variable nicht wie gewünscht zurück, sondern das Klassenobjekt.

Das Aufrufen eines Konstruktors oder eines Destruktors ist eine sehr, sehr, sehr schlechte Programmierpraxis, unabhängig davon, in welcher Sprache Sie sich befinden, und sollte niemals durchgeführt werden.

Konstruktoren sollten auch Objekte konstruieren, um sie gebrauchsfertig zu initialisieren, nicht für die eigentliche Arbeit. Die vom Objekt auszuführende Arbeit sollte in einer separaten Funktion erfolgen.

Statische Klassenmethoden, die überhaupt nicht instanziiert / initialisiert werden müssen

Wenn es sich bei Ihrer Klassenmethode um eine statische Klassenmethode handelt, können Sie den Namen der Klasse in Anführungszeichen anstatt $thiswie unten gezeigt übergeben:

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

Closures & PHP 5.3

Leider können Sie nicht vermeiden, dass die Linie die neue Klasse erstellt. Die einzige andere Lösung zum Überspringen wäre Kesselplattencode, der noch diese Zeile enthält, und würde PHP 5.3+ erfordern, zB:

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

An diesem Punkt können Sie die Klasse auch überspringen und einfach eine Funktion verwenden:

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

Denken Sie jedoch daran, dass Sie jetzt das Gespenst der anonymen Funktionen eingeführt haben. Es gibt keine Möglichkeit, die oben genannte Aktion mit zu entfernen. remove_actionDies kann und bereitet Entwicklern, die mit dem Code anderer Leute arbeiten müssen, große Schmerzen.

Auf kaufmännischem und

Möglicherweise werden folgende Aktionen angezeigt:

array( &$this, 'getStuffDone' );

Das ist schlimm . &wurde wieder in PHP 4 hinzugefügt, als Objekte als Werte und nicht als Referenzen übergeben wurden. PHP 4 ist mehr als ein Jahrzehnt alt und wurde von WordPress seit langem nicht mehr unterstützt.

Es gibt keinen Grund , &thisbeim Hinzufügen von Hooks und Filtern zu verwenden, und das Entfernen der Referenz verursacht keine Probleme und kann sogar die Kompatibilität mit zukünftigen Versionen von PHP verbessern

Verwenden Sie dies stattdessen:

array( $this, 'getStuffDone' );

Okay. Vielen Dank von all dem; wirklich einiges gelernt. Ich fühle mich jetzt wirklich nur in klassenbasiertem PHP wohl. Dies ist, was ich getan habe und es funktioniert, aber können Sie mir sagen, ob es in irgendeiner Weise falsch / falsch ist? Ich habe die Klasse innerhalb einer statischen Funktion innerhalb der Klasse selbst initiiert. Dann referenzierte die statische Funktion in der add_action. Siehe diesen Link: pastebin.com/0idyPwwY
Matthew Ruddy

Ja, Sie könnten es auf diese Weise tun, obwohl ich vermeiden könnte, $classals Variablennamen zu verwenden, sind diese Wörter eher reserviert. Ich denke, Sie geben sich viel Mühe, um zu vermeiden, etwas Ähnliches wie $x = new Y();im globalen Rahmen zu sagen , und Sie fügen Komplexität hinzu, wo keine Notwendigkeit besteht. Ihr Versuch, die Menge des geschriebenen Codes zu reduzieren, hat das Schreiben von mehr Code erforderlich gemacht!
Tom J Nowell

In allen oben genannten Fällen ist es besser, eine Funktion als eine Klasse zu verwenden, da diese Klasse ohnehin verworfen wird und dem gleichen Zweck dient. Es ist eine Verschwendung von Ressourcen. Denken Sie daran, dass das Erstellen und Zerstören eines Objekts Kosten verursacht. Sie möchten nur wenige Objekte, und Sie möchten, dass sie eine lange Lebensdauer haben
Tom J Nowell

Guter Punkt. Die Art, wie ich es betrachte, hat sich geändert. Ich denke, ich werde es stattdessen im globalen Bereich aufrufen und den zusätzlichen Code vermeiden.
Matthew Ruddy

1
Ich möchte hinzufügen, dass, wenn Sie Ihre Klasse in einen Namespace gesetzt haben, Sie das auch hinzufügen müssen oder add_action () / add_filter () es nicht finden werden - wie add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
folgt

10

Beispielklasse

Anmerkungen:

  • Initialisiere die Klasse nur einmal
    • Rufen Sie die Priorität 0 auf, damit Sie später denselben Hook mit der Standardpriorität verwenden können
    • Wickeln Sie es in ein ! class_exists, um zu vermeiden, dass es zweimal aufgerufen wird, und platzieren Sie den Init-Aufrufer darin
  • Machen Sie die initFunktion und die Klasse varstatic
  • Rufen Sie den Konstruktor in Ihrem Init auf, wenn Sie die Klasse aufrufen new self.

Hier ist ein Beispiel

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+

Bitte lassen Sie das &aus. Wir sind schon jenseits von 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();
}

Bitte bearbeiten Sie Ihre Antwort und fügen Sie eine Erklärung hinzu: Warum könnte dies das Problem lösen?
fuxia

0

Sie können Ereignisse in Ihrer Klasse auslösen, ohne sie zuerst laden zu müssen . Dies ist praktisch, wenn Sie nicht die gesamte Klasse im Voraus laden möchten, sondern auf die WordPress-Filter und -Aktionen zugreifen müssen.

Hier ist ein sehr einfaches Beispiel

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

Dies ist eine sehr vereinfachte Version von Kaisers Antwort, zeigt aber in einfachen Worten das Verhalten. Könnte nützlich sein für diejenigen, die sich zum ersten Mal mit diesem Stil beschäftigen.

Andere Methoden können das Objekt bei Bedarf dennoch initiieren. Ich persönlich benutze diese Methode, um es optionalen Teilen meines Plugins zu ermöglichen, Skripte in die Warteschlange zu stellen, aber nur das Objekt bei einer AJAX-Anfrage auszulösen.


0

Das funktioniert bei mir:

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

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

0

Im Allgemeinen würden Sie einem Hook keine ganze Klasse hinzufügen . Der add_action()/ add_filter()Haken erwartet Rückruffunktionen , die können aus referenziert werden innerhalb einer Klasse .

Angenommen, Sie haben eine init()Funktion in Ihrer Klasse, die Sie in den WordPress- initHook einbinden möchten .

Platzieren Sie Ihren add_action()Anruf in Ihrer Klasse und identifizieren Sie den Rückruf folgendermaßen:

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

(Hinweis: Ich gehe davon aus, dass Ihre Klasse einen korrekten Namespace hat. Andernfalls müssen Sie sicherstellen, dass Ihre Rückruffunktionen einen Namespace haben.)


Was ist, wenn die aktuelle Klasse noch nicht initiiert wurde? Ich habe versucht, add_action zu verwenden, um tatsächlich zu initiieren, also muss ich nicht $ var = new MyClass () hinzufügen. vorher woanders.
Matthew Ruddy

Können Sie nicht einfach einen Rückruf schreiben, um Ihre Klasse zu instanziieren init(oder wo immer Sie es brauchen)?
Chip Bennett

0

Sie sollten in der Lage sein, dies zu tun, indem Sie den Klassennamen anstelle des instanziierten Objekts übergeben:

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

(Theoretisch sollte auch Ihre andere Lösung funktionieren

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

Ich bin mir nicht sicher, warum es nicht so ist. Vielleicht, wenn Sie nicht per Referenz anrufen?)


Erstens funktioniert man nicht. Lässt die Seite einfach leer werden. Der zweite funktioniert, aber nur, weil die Klasse in der ersten Zeile initiiert wurde. Es besiegt den Zweck des Hinzufügens der Aktion, da die Klasse bereits initiiert wurde. Aber es macht nicht das, was ich versuche, was die Klasse durch die Aktion initiiert. In dem Code, an dem ich gerade arbeite, ist die Aktion nicht "admin_init", sondern eine benutzerdefinierte Aktion in einer anderen Klasse. Ich möchte nicht, dass die Funktion 'MyClass' ausgelöst wird, wenn die Aktion in der anderen Klasse nicht vorhanden ist. Entschuldigung, wenn ich etwas verpasse; Lernen, wie ich gehe
Matthew Ruddy

Ja, ich habe mich geirrt. Das funktioniert nur, wenn Sie eine statische initMethode aufrufen . Siehe wordpress.stackexchange.com/a/48093/14052
Boone Gorges
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.