Come fare riferimento a una classe in add_action in WordPress
È 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.

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' ]

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

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!

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

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

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' ) );

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 classestatic
- 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. :)

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.

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

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

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.

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

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.

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

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.

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

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 ) {
}
}
