add_action fa riferimento a una classe


38

È possibile fare riferimento a una classe anziché a una funzione in 'add_action'? Non riesco a capirlo. Ecco solo un esempio di base della funzione in questione.

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

Quindi sì, non funziona. Ho anche provato:

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

E:

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

E anche:

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

Posso comunque farlo senza dover creare una funzione separata che carichi la classe? Mi piacerebbe poter eseguire il costruttore di classi tramite add_action. Questo è tutto ciò che deve essere caricato per far rotolare la palla.

Risposte:


69

No, non è possibile "inizializzare" o creare un'istanza della classe tramite un hook, non direttamente. È sempre necessario un codice aggiuntivo (e non è una cosa desiderabile poterlo fare, poiché stai aprendo una lattina di worm per te stesso.

Ecco un modo migliore per farlo:

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

Naturalmente si potrebbe creare una classe di interfaccia per semplificarla ulteriormente per il caso generale:

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

Si noti che come interfaccia, non è possibile creare direttamente un oggetto di questo tipo, ma è possibile creare una sottoclasse, che consente di dire:

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

Che quindi aggiungerebbe automaticamente tutti gli hook, devi solo definire cosa si sta facendo esattamente in una sottoclasse e quindi crearlo!

Sui costruttori

Non aggiungerei un costruttore come funzione hook, è una cattiva pratica e può condurre molti altri eventi insoliti. Inoltre nella maggior parte delle lingue un costruttore restituisce l'oggetto che viene istanziato, quindi se il tuo hook deve restituire qualcosa come un filtro, non restituirà la variabile filtrata come desideri, ma restituirà invece l'oggetto della classe.

Chiamare un costruttore o un distruttore è una pratica di programmazione molto, molto, pessima, indipendentemente dal linguaggio in cui ti trovi, e non dovrebbe mai essere fatto.

I costruttori dovrebbero anche costruire oggetti, per inizializzarli pronti per l'uso, non per il lavoro effettivo. Il lavoro che deve essere svolto dall'oggetto dovrebbe essere in una funzione separata.

Metodi di classe statici e non è necessario creare un'istanza / inizializzazione

Se il tuo metodo di classe è un metodo di classe statico, puoi passare il nome della classe tra virgolette anziché $thiscome mostrato di seguito:

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

Chiusure e PHP 5.3

Purtroppo non puoi evitare la linea che crea la nuova classe. L'unica altra soluzione per saltarla riguarderebbe il codice della piastra della caldaia che ha ancora quella linea e richiederebbe PHP 5.3+, ad esempio:

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

A quel punto puoi anche saltare la classe e usare solo una funzione:

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

Ma tieni presente che ora hai introdotto lo spettro di funzioni anonime. Non è possibile rimuovere l'azione sopra descritta remove_actione ciò può causare problemi agli sviluppatori che devono lavorare con il codice di altre persone.

Su e commerciali

Potresti vedere azioni usate in questo modo:

array( &$this, 'getStuffDone' );

Questo è male . &è stato aggiunto in PHP 4 quando gli oggetti sono stati passati come valori, non come riferimenti. PHP 4 ha più di un decennio e non è supportato da WordPress da molto tempo.

Non vi è motivo di utilizzare &thisquando si aggiungono hook e filtri e la rimozione del riferimento non causerà problemi e potrebbe persino migliorare la compatibilità con le versioni future di PHP

Usa questo invece:

array( $this, 'getStuffDone' );

Ok. Grazie di tutto ciò; ho davvero imparato un bel po '. Adesso mi sto davvero sentendo a mio agio con PHP di classe. Questo è quello che ho fatto e funziona, ma potresti dirmi se è una cattiva pratica / scorretta in qualche modo? Ho avviato la classe all'interno di una funzione statica, all'interno della classe stessa. Quindi ha fatto riferimento alla funzione statica in add_action. Vedi questo link: pastebin.com/0idyPwwY
Matthew Ruddy,

sì, potresti farlo in questo modo, anche se potrei evitare di usare $classcome nome della tua variabile, quelle parole tendono ad essere riservate. Penso che stai facendo di tutto per evitare di dire qualcosa di simile $x = new Y();nell'ambito globale e stai aggiungendo complessità laddove non è necessario. Il tuo tentativo di ridurre la quantità di codice scritto ha comportato la scrittura di più codice!
Tom J Nowell

Sottolineerei in tutti i casi precedenti che sarebbe meglio usare una funzione piuttosto che una classe, poiché quella classe verrà comunque scartata e avrà lo stesso scopo. È uno spreco di risorse. Ricorda, c'è un costo per creare e distruggere un oggetto, vuoi pochi oggetti e vuoi che durino a lungo
Tom J Nowell

Buon punto. Modificato il modo in cui l'ho visto. Penso che lo chiamerò invece nell'ambito globale, evitando il codice aggiuntivo.
Matthew Ruddy,

1
Vorrei aggiungere che se ti capita di mettere la tua classe in uno spazio dei nomi, dovrai aggiungere anche quello o add_action () / add_filter () non lo troverà - in questo modo:add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );
jschrab

10

Classe di esempio

Gli appunti:

  • Iniziare la lezione una sola volta
    • Chiama la priorità 0, in modo da poter utilizzare lo stesso hook con la priorità predefinita in un secondo momento
    • Avvolgetelo in a ! class_existsper evitare di chiamarlo due volte e mettete dentro il chiamante init
  • Rendi la initfunzione e la classe varstatic
  • Chiama il costruttore dall'interno del tuo init, quando chiami la classe new self.

Ecco un esempio

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+

Per favore , lascia &fuori. Siamo già oltre 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();
}

Si prega di modificare la risposta , e aggiungere una spiegazione: perché potrebbe che risolvere il problema?
fuxia

0

Puoi attivare eventi nella tua classe senza la necessità di caricarli inizialmente . Ciò è utile se non si desidera caricare l'intera classe in anticipo, ma è necessario accedere ai filtri e alle azioni di WordPress.

Ecco un esempio molto semplice

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

Questa è una versione molto semplificata della risposta di Kaiser ma mostra in termini semplici il comportamento. Potrebbe essere utile per chi guarda questo stile per la prima volta.

Altri metodi possono comunque avviare l'oggetto, se necessario. Personalmente sto usando questo metodo per consentire a parti facoltative del mio plug-in di accodare gli script, ma solo innescando l'oggetto su una richiesta AJAX.


0

Questo funziona per me:

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

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

0

In generale, non aggiungeresti un'intera classe a un hook. I add_action()/ add_filter()hook si aspettano funzioni di callback , a cui si può fare riferimento all'interno di una classe .

Diciamo che hai una init()funzione all'interno della tua classe, che vuoi agganciare al gancio di WordPress init.

Inserisci la tua add_action()chiamata all'interno della tua classe, quindi identifica la callback in questo modo:

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

(Nota: suppongo che la tua classe sia correttamente spaziata dai nomi; altrimenti, assicurati di spaziare le tue funzioni di callback.)


Che dire se la classe attuale non è già stata avviata? Stavo cercando di utilizzare add_action per avviare effettivamente, quindi non devo aggiungere $ var = new MyClass (); in anticipo altrove.
Matthew Ruddy,

Non puoi semplicemente scrivere un callback per creare un'istanza della tua classe init(o ovunque tu ne abbia bisogno)?
Chip Bennett,

0

Dovresti essere in grado di farlo passando il nome della classe anziché l'oggetto istanziato:

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

(In teoria, anche la tua altra soluzione dovrebbe funzionare

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

Non sono sicuro perché non lo faccia. Forse se non chiami per riferimento?)


Il primo non funziona. Rende la pagina vuota. Il secondo funziona, ma solo perché la classe è stata avviata nella prima riga. In un certo senso sconfigge lo scopo di aggiungere l'azione, perché la classe è già stata avviata. Ma non fa quello che sto cercando di fare, ovvero avviare la classe attraverso l'azione. Nel codice su cui sto lavorando, l'azione non è "admin_init" ma un'azione personalizzata all'interno di un'altra classe. Non voglio che la funzione 'MyClass' venga avviata se l'azione nell'altra classe non è presente. Scusa se mi manca qualcosa; imparando mentre vado
Matthew Ruddy,

Sì, ho sbagliato. Funziona solo se stai chiamando un initmetodo statico . Vedi wordpress.stackexchange.com/a/48093/14052
Boone Gorges
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.