Come fare riferimento a una classe in add_action in WordPress

5 apr 2012, 16:44:22
Visualizzazioni: 92K
Voti: 58

È possibile fare riferimento a una classe invece di una funzione in 'add_action'? Non riesco a capire come fare. Ecco un esempio base della funzione in questione.

add_action( 'admin_init', 'MyClass' );
class MyClass {
     function __construct() {
          // Qui è dove vengono eseguite le operazioni
     }
}

Quindi no, questo non funziona. Ho anche provato:

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

E anche:

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

E inoltre:

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

Esiste un modo per fare questo senza dover creare una funzione separata che carica la classe? Vorrei poter semplicemente eseguire il costruttore della classe attraverso add_action. È tutto ciò che serve per far partire il processo.

0
Tutte le risposte alla domanda 9
6
97

No, non è possibile 'inizializzare' o istanziare la classe tramite un hook, non direttamente.

È sempre necessario del codice aggiuntivo (e non è nemmeno una cosa auspicabile da fare, perché si rischia di aprire un vaso di Pandora).

Ecco alcuni modi migliori per farlo:

class MyClass {
     function __construct() {
          add_action( 'admin_init', [ $this, 'getStuffDone' ] );
     }
     function getStuffDone() {
          // .. Qui è dove le cose vengono fatte ..
     }
}
$var = new MyClass();

Ovviamente si potrebbe creare una classe interfaccia per semplificare ulteriormente il caso generale:

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

Nota che come interfaccia, non puoi creare un oggetto di questo tipo direttamente, ma potresti creare una sottoclasse, permettendoti di fare:

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

Il quale aggiungerebbe automaticamente tutti gli hook, devi solo definire cosa esattamente viene fatto in una sottoclasse e poi crearla!

Sui Costruttori

Non aggiungerei un costruttore come funzione hook, è una cattiva pratica e può portare a molti eventi insoliti. Inoltre, nella maggior parte dei linguaggi un costruttore restituisce l'oggetto che viene istanziato, quindi se il tuo hook ha bisogno di restituire qualcosa come in un filtro, non restituirà la variabile filtrata come desideri, ma invece restituirà l'oggetto della classe.

Chiamare direttamente un costruttore o un distruttore è una pessima pratica di programmazione, in qualsiasi linguaggio, e non dovrebbe mai essere fatto.

I costruttori dovrebbero anche costruire oggetti, per inizializzarli e renderli pronti all'uso, non per il lavoro effettivo. Il lavoro da fare dall'oggetto dovrebbe essere in una funzione separata.

Metodi statici della classe e non aver bisogno di istanziare/inizializzare affatto

Se il metodo della tua classe è un metodo statico, puoi passare il nome della classe tra virgolette invece di $this come mostrato di seguito:

class MyClass {
    public static function getStuffDone() {
        // .. Qui è dove le cose vengono fatte ..
    }
}
add_action( 'admin_init', [ __NAMESPACE__ . '\MyClass','getStuffDone' ] );

Nota l'uso di __NAMESPACE__ che è necessario se la tua classe è all'interno di un namespace.

Closures

Sfortunatamente non puoi evitare la riga che crea la nuova classe. L'unica altra soluzione per saltarla comporterebbe del codice boilerplate che avrebbe comunque quella riga, ad esempio:

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

A quel punto potresti anche saltare la classe e usare direttamente una funzione:

add_action( 'admin_init', function() {
   // fare cose
} );

Ma tieni presente che hai ora introdotto lo spettro delle funzioni anonime. Non c'è modo di rimuovere l'azione sopra usando remove_action, e questo può causare grandi problemi agli sviluppatori che devono lavorare con il codice di altre persone.

Sulle Ampersand

Potresti vedere le azioni usate così:

array( &$this, 'getStuffDone' )

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

Non c'è alcuna ragione per usare &this quando si aggiungono hook e filtri, e rimuovere il riferimento non causerà problemi, anzi potrebbe migliorare la compatibilità con le versioni future di PHP.

Usa invece questo:

[ $this, 'getStuffDone' ]
5 apr 2012 17:16:22
Commenti

Ok. Grazie per tutto questo; ho davvero imparato parecchio. Ora mi sto abituando alla programmazione orientata agli oggetti in PHP. Questo è quello che ho fatto, e funziona, ma potresti dirmi se è una cattiva pratica/errato in qualche modo? Ho inizializzato la classe all'interno di una funzione statica, dentro la classe stessa. Poi ho richiamato la funzione statica nell'add_action. Vedi questo link: http://pastebin.com/0idyPwwY

Matthew Ruddy Matthew Ruddy
5 apr 2012 17:35:30

sì, potresti farlo in quel modo, anche se eviterei di usare $class come nome della tua variabile, quelle parole tendono ad essere riservate. Penso che tu stia facendo molti giri per evitare di scrivere qualcosa di simile a $x = new Y(); nello scope globale, e stai aggiungendo complessità dove non è necessaria. Il tuo tentativo di ridurre la quantità di codice scritto ha comportato la scrittura di più codice!

Tom J Nowell Tom J Nowell
5 apr 2012 17:45:59

Vorrei far notare che in tutti i casi sopra citati, sarebbe meglio usare una funzione piuttosto che una classe, poiché quella classe verrà comunque scartata e serve allo stesso scopo. È uno spreco di risorse. Ricorda, c'è un costo nel creare e distruggere un oggetto, vuoi pochi oggetti e che siano longevi

Tom J Nowell Tom J Nowell
5 apr 2012 17:47:28

Ottimo punto. Ho cambiato il modo in cui lo stavo vedendo. Penso che lo chiamerò nello scope globale invece, evitando il codice extra.

Matthew Ruddy Matthew Ruddy
5 apr 2012 18:09:41

Vorrei aggiungere che se per caso hai messo la tua classe in un namespace, dovrai aggiungere anche quello altrimenti add_action()/add_filter() non la troveranno - così: add_action( 'admin_init', array('MyNamespace\MyClass','getStuffDone' ) );

jschrab jschrab
28 apr 2018 01:01:39

Cosa succederebbe se add_action() fosse dentro construct() nella stessa classe?

MMTDesigner MMTDesigner
19 giu 2021 15:45:20
Mostra i restanti 1 commenti
0
12

Classe di esempio

Note:

  • Inizializza la classe solo una volta
    • Chiamala con priorità 0, così puoi usare lo stesso hook con la priorità di default successivamente
    • Racchiudila in un ! class_exists per evitare di chiamarla due volte e inserisci il chiamante di init all'interno
  • Rendi la funzione init e la variabile di classe static
  • Chiama il costruttore dall'interno del tuo init, quando chiami la classe con new self.

Ecco un esempio

if ( ! class_exists( 'WPSESampleClass' ) )
{
    // Inizializza la classe con priorità 0 per evitare di aggiungere priorità dentro la classe dove il default è 10
    add_action( 'init', array ( 'WPSESampleClass', 'init' ), 0 );

class WPSESampleClass
{
    /**
     * L'oggetto della classe
     */
    static private $class = null;

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

        return self :: $class;
    }

    public function __construct()
    {
        // esegui operazioni come chiamate ad action:
        add_action( 'init', array( $this, 'cb_fn_name' ) );
    }

    public function cb_fn_name()
    {
        // esegui operazioni 
    }
} // END Class WPSESampleClass

} // endif;

Php 5+

Per favore, lascia fuori il &. Siamo già oltre php4. :)

5 apr 2012 17:16:16
0

Puoi attivare eventi nella tua classe senza la necessità di caricarla inizialmente. Questo è utile se non vuoi caricare l'intera classe all'inizio, ma hai bisogno di 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 . 'Funziona senza caricare l\'oggetto';
    }
}

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 ancora inizializzare l'oggetto se necessario. Personalmente uso questo metodo per permettere a parti opzionali del mio plugin di accodare script, ma attivando l'oggetto solo su una richiesta AJAX.

22 dic 2014 16:02:08
1
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 }
    }// fine della classe
}// fine if

if (class_exists("AllInOneWoo")){       
    $all_in_one_woo = new AllInOneWoo();
}
4 mar 2018 10:14:23
Commenti

Per favore [modifica] la tua risposta e aggiungi una spiegazione: perché questa soluzione potrebbe risolvere il problema?

fuxia fuxia
4 mar 2018 16:06:06
0

Questo funziona per me:

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

add_action('foo_bar', array('foo', 'bar'));
17 mar 2015 20:25:38
1

Risposta 2025

Puoi assolutamente farlo ora con il __invoke metodo magico. Questo viene chiamato quando provi a chiamare la classe come una funzione.

class MyHandler {
  public function __construct(){ /* ... */ }

  public function __invoke(){
    // qualunque cosa tu voglia fare qui
  }
}

add_action('admin_init', new MyHandler);
5 mar 2025 22:22:13
Commenti

Anche se penso che questa sia una risposta interessante, bisognerebbe valutare se sia davvero la strada migliore per il proprio caso d'uso. Personalmente, non riesco a pensare a un caso d'uso in cui questa soluzione sarebbe più leggibile o "migliore" rispetto alle alternative presenti in questo argomento.

Howdy_McGee Howdy_McGee
5 mar 2025 22:31:07
2

In generale, non aggiungeresti un'intera classe a un hook. Gli hook add_action()/add_filter() si aspettano callback come funzioni, che possono essere referenziate da all'interno di una classe.

Supponiamo che tu abbia una funzione init() all'interno della tua classe, che desideri agganciare all'hook init di WordPress.

Metti la tua chiamata add_action() all'interno della tua classe e identifica il callback in questo modo:

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

(Nota: assumo che la tua classe sia correttamente namespaced; in caso contrario, assicurati di namespacciare le tue funzioni callback.)

5 apr 2012 17:03:00
Commenti

Cosa succede se la classe corrente non è già stata inizializzata? Stavo cercando di usare add_action per inizializzarla effettivamente, così non devo aggiungere $var = new MyClass(); in precedenza da un'altra parte.

Matthew Ruddy Matthew Ruddy
5 apr 2012 17:08:41

Non puoi semplicemente scrivere un callback per istanziare la tua classe su init (o ovunque ti serva)?

Chip Bennett Chip Bennett
5 apr 2012 18:55:28
2

Dovresti essere in grado di farlo passando il nome della classe invece dell'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 al volo del perché non funzioni. Forse se non chiami per riferimento?)

5 apr 2012 17:09:54
Commenti

Il primo non funziona. Fa semplicemente diventare la pagina vuota. Il secondo funziona, ma solo perché la classe è stata inizializzata nella prima riga. In un certo senso va contro lo scopo di aggiungere l'azione, perché la classe è già stata inizializzata. Ma non fa quello che sto cercando di fare, cioè inizializzare la classe tramite 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 inizializzata se l'azione nell'altra classe non è presente. Scusa se mi sfugge qualcosa; sto imparando man mano.

Matthew Ruddy Matthew Ruddy
5 apr 2012 17:15:26

Sì, mi sbagliavo. Funziona solo se stai chiamando un metodo init statico. Vedi http://wordpress.stackexchange.com/a/48093/14052

Boone Gorges Boone Gorges
5 apr 2012 18:17:59
0

Esempio di metodo statico

class FrmTransLiteEntriesController {

    /**
     * Includi i dettagli del pagamento nella sidebar dell'entry.
     *
     * @param stdClass $entry
     * @return void
     */
    public static function sidebar_list( $entry ) {
    

        add_action( 'frm_entry_shared_sidebar_middle', __CLASS__ . '::show_sidebar_list' );
    }

    
    public static function show_sidebar_list( $entry ) {
    }
}
16 set 2023 10:25:33