Qual è il modo migliore per inizializzare una classe in un plugin WordPress?
Ho creato un plugin e, naturalmente, ho voluto seguire un approccio orientato agli oggetti (OO). Finora ho creato questa classe e subito dopo ho creato un'istanza della stessa:
class ClassName {
public function __construct(){
}
}
$class_instance = new ClassName();
Presumo ci sia un modo più "WordPress" per inizializzare questa classe, e poi mi sono imbattuto in persone che dicono di preferire una funzione init()
rispetto a una __construct()
. Similmente, ho trovato alcune persone che utilizzano il seguente hook:
class ClassName {
public function init(){
}
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );
Qual è generalmente considerato il modo migliore per creare un'istanza di una classe WordPress al caricamento e averla come variabile accessibile globalmente?
NOTA: Come punto interessante, ho notato che mentre register_activation_hook()
può essere chiamato dall'interno del __construct
, non può essere chiamato dall'interno di init()
usando il secondo esempio. Forse qualcuno potrebbe chiarirmi questo punto.
Modifica: Grazie per tutte le risposte, c'è chiaramente un bel po' di dibattito su come gestire l'inizializzazione all'interno della classe stessa, ma penso ci sia generalmente un buon consenso sul fatto che add_action( 'plugins_loaded', ...);
sia il modo migliore per avviarla...
Modifica: Solo per confondere le cose, ho anche visto usare questo metodo (anche se non lo userei personalmente perché trasformare una classe ben strutturata OO in una funzione sembra vanificare lo scopo):
// Avvia questo plugin
add_action( 'init', 'ClassName' );
function ClassName() {
global $class_name;
$class_name = new ClassName();
}

Arrivato qui esattamente 2 anni dopo che la domanda originale è stata posta, ci sono alcune cose che voglio sottolineare. (Non chiedetemi mai di sottolineare molte cose, mai).
Hook appropriato
Per istanziare una classe di un plugin, dovrebbe essere usato l'appropriato hook. Non esiste una regola generale su quale sia, perché dipende da ciò che la classe fa.
Usare un hook molto precoce come "plugins_loaded"
spesso non ha senso perché un hook del genere viene attivato per richieste admin, frontend e AJAX, ma molto spesso un hook successivo è molto meglio perché permette di istanziare le classi del plugin solo quando servono.
Ad esempio, una classe che fa cose per i template può essere istanziata su "template_redirect"
.
In generale, è molto raro che una classe debba essere istanziata prima che "wp_loaded"
sia stato attivato.
Nessuna Classe Dio
La maggior parte delle classi usate come esempio nelle risposte più vecchie usa una classe chiamata tipo "Prefix_Example_Plugin"
o "My_Plugin"
... Questo indica che probabilmente c'è una classe principale per il plugin.
Beh, a meno che un plugin non sia fatto da una singola classe (nel qual caso chiamarla come il nome del plugin è assolutamente ragionevole), creare una classe che gestisce l'intero plugin (ad esempio aggiungendo tutti gli hook di cui un plugin ha bisogno o istanziando tutte le altre classi del plugin) può essere considerata una cattiva pratica, come esempio di un god object.
Nella programmazione orientata agli oggetti il codice dovrebbe tendere ad essere S.O.L.I.D. dove la "S" sta per "Single responsibility principle".
Significa che ogni classe dovrebbe fare una sola cosa. Nello sviluppo di plugin WordPress significa che gli sviluppatori dovrebbero evitare di usare un singolo hook per istanziare una classe principale del plugin, ma dovrebbero usare hook diversi per istanziare classi diverse, in base alla responsabilità della classe.
Evitare hook nel costruttore
Questo argomento è stato introdotto in altre risposte qui, tuttavia voglio ribadire questo concetto e linkare questa altra risposta dove è stato spiegato piuttosto ampiamente nel contesto dei test unitari.
Quasi 2015: PHP 5.2 è per zombie
Dal 14 Agosto 2014, PHP 5.3 ha raggiunto la fine del suo ciclo di vita. È definitivamente morto. PHP 5.4 sarà supportato per tutto il 2015, cioè per un altro anno al momento in cui scrivo.
Tuttavia, WordPress supporta ancora PHP 5.2, ma nessuno dovrebbe scrivere una singola riga di codice che supporti quella versione, specialmente se il codice è OOP.
Ci sono diverse ragioni:
- PHP 5.2 è morto da molto tempo, non vengono rilasciati fix di sicurezza per esso, il che significa che non è sicuro
- PHP 5.3 ha aggiunto molte feature a PHP, funzioni anonime e namespace über alles
- le versioni più recenti di PHP sono molto più veloci. PHP è gratuito. Aggiornarlo è gratuito. Perché usare una versione più lenta e insicura se puoi usarne una più veloce e sicura gratuitamente?
Se non vuoi usare codice PHP 5.4+, usa almeno 5.3+
Esempio
A questo punto è tempo di rivedere le risposte più vecchie basandoci su quanto detto finora.
Una volta che non dobbiamo più preoccuparci di 5.2, possiamo e dovremmo, usare i namespace.
Per spiegare meglio il principio di responsabilità singola, il mio esempio userà 3 classi, una che fa qualcosa sul frontend, una sul backend e una terza usata in entrambi i casi.
Classe Admin:
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Classe Frontend:
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Interfaccia Tools:
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
E una classe Tools, usata dalle altre due:
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
Avendo queste classi, posso istanziarle usando gli hook appropriati. Qualcosa tipo:
require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';
add_action( 'admin_init', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $admin_stuff; // questo non è ideale, il motivo è spiegato sotto
$admin_stuff = new GM\WPSE\Example\AdminStuff( $tools );
} );
add_action( 'template_redirect', function() {
require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
$tools = new GM\WPSE\Example\Tools;
global $front_stuff; // questo non è ideale, il motivo è spiegato sotto
$front_stuff = new GM\WPSE\Example\FrontStuff( $tools );
} );
Inversione delle Dipendenze & Iniezione delle Dipendenze
Nell'esempio sopra ho usato namespace e funzioni anonime per istanziare diverse classi su hook diversi, mettendo in pratica quanto detto sopra.
Nota come i namespace permettono di creare classi nominate senza alcun prefisso.
Ho applicato un altro concetto che era stato menzionato indirettamente sopra: Iniezione delle Dipendenze, è un metodo per applicare il Principio di Inversione delle Dipendenze, la "D" nell'acronimo SOLID.
La classe Tools
è "iniettata" nelle altre due classi quando vengono istanziate, così in questo modo è possibile separare le responsabilità.
Inoltre, le classi AdminStuff
e FrontStuff
usano type hinting per dichiarare che hanno bisogno di una classe che implementa ToolsInterface
.
In questo modo noi stessi o gli utenti che usano il nostro codice possono usare diverse implementazioni della stessa interfaccia, rendendo il nostro codice non accoppiato a una classe concreta ma a un'astrazione: questo è esattamente ciò di cui tratta il Principio di Inversione delle Dipendenze.
Tuttavia, l'esempio sopra può essere ulteriormente migliorato. Vediamo come.
Autoloader
Un buon modo per scrivere codice OOP più leggibile è non mischiare la definizione dei tipi (Interfacce, Classi) con altro codice, e mettere ogni tipo nel suo file.
Questa regola è anche uno degli standard di codifica PSR-11.
Tuttavia, facendo così, prima di poter usare una classe bisogna richiedere il file che la contiene.
Questo può essere eccessivo, ma PHP fornisce funzioni di utilità per caricare automaticamente una classe quando è richiesta, usando una callback che carica un file basandosi sul suo nome.
Usando i namespace diventa molto semplice, perché ora è possibile far corrispondere la struttura delle cartelle con la struttura dei namespace.
Questo non è solo possibile, ma è anche un altro standard PSR (o meglio 2: PSR-0 ora deprecato, e PSR-4).
Seguendo questi standard è possibile utilizzare diversi tool che gestiscono l'autoload, senza dover codificare un autoloader personalizzato.
Devo dire che gli standard di codifica WordPress hanno regole diverse per i nomi dei file.
Quindi quando si scrive codice per il core di WordPress, gli sviluppatori devono seguire le regole WP, ma quando si scrive codice personalizzato è una scelta dello sviluppatore, ma usare lo standard PSR è più semplice per usare tool già scritti2.
Accesso Globale, Pattern Registry e Service Locator.
Uno dei problemi più grandi quando si istanziano classi di plugin in WordPress, è come accedervi da varie parti del codice.
WordPress stesso usa l'approccio globale: le variabili sono salvate nello scope globale, rendendole accessibili ovunque. Ogni sviluppatore WordPress scrive la parola global
migliaia di volte nella sua carriera.
Questo è anche l'approccio che ho usato per l'esempio sopra, ma è malvagio.
Questa risposta è già troppo lunga per permettermi di spiegare ulteriormente perché, ma leggere i primi risultati nei SERP per "global variables evil" è un buon punto di partenza.
Ma come è possibile evitare le variabili globali?
Ci sono diversi modi.
Alcune delle risposte più vecchie qui usano l'approccio istanza statica.
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
È semplice e abbastanza buono, ma forza a implementare il pattern per ogni classe a cui vogliamo accedere.
Inoltre, molte volte questo approccio porta a cadere nel problema della classe dio, perché gli sviluppatori rendono accessibile una classe principale usando questo metodo, e poi la usano per accedere a tutte le altre classi.
Ho già spiegato quanto sia cattiva una classe dio, quindi l'approccio dell'istanza statica è un buon modo per procedere quando un plugin deve rendere accessibili solo una o due classi.
Questo non significa che può essere usato solo per plugin con un paio di classi, infatti, quando il principio di iniezione delle dipendenze è usato correttamente, è possibile creare applicazioni piuttosto complesse senza la necessità di rendere globalmente accessibili un gran numero di oggetti.
Tuttavia, a volte i plugin hanno bisogno di rendere accessibili alcune classi, e in quel caso l'approccio dell'istanza statica è eccessivo.
Un altro possibile approccio è usare il pattern registry.
Questa è una implementazione molto semplice di esso:
namespace GM\WPSE\Example;
class Registry {
private $storage = array();
function add( $id, $class ) {
$this->storage[$id] = $class;
}
function get( $id ) {
return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
}
}
Usando questa classe è possibile salvare oggetti nell'oggetto registry tramite un id, quindi avendo accesso a un registry è possibile accedere a tutti gli oggetti. Ovviamente quando un oggetto è creato per la prima volta deve essere aggiunto al registry.
Esempio:
global $registry;
if ( is_null( $registry->get( 'tools' ) ) ) {
$tools = new GM\WPSE\Example\Tools;
$registry->add( 'tools', $tools );
}
if ( is_null( $registry->get( 'front' ) ) ) {
$front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );
$registry->add( 'front', front_stuff );
}
add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );
L'esempio sopra rende chiaro che per essere utile il registry deve essere globalmente accessibile. Una variabile globale per il solo registry non è molto male, tuttavia per i puristi non-global è possibile implementare l'approccio dell'istanza statica per un registry, o magari una funzione con una variabile statica:
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
La prima volta che la funzione è chiamata istanzierà il registry, nelle chiamate successive lo restituirà semplicemente.
Un altro metodo specifico di WordPress per rendere una classe globalmente accessibile è restituire un'istanza di un oggetto da un filtro. Qualcosa tipo:
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
Dopo di che ovunque il registry sia necessario:
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
Un altro pattern che può essere usato è il service locator pattern. È simile al pattern registry, ma i service locator sono passati a varie classi usando l'iniezione delle dipendenze.
Il problema principale con questo pattern è che nasconde le dipendenze delle classi rendendo il codice più difficile da mantenere e leggere.
Contenitori DI
Indipendentemente dal metodo usato per rendere il registry o il service locator globalmente accessibili, gli oggetti devono essere salvati lì, e prima di essere salvati devono essere istanziati.
In applicazioni complesse, dove ci sono parecchie classi e molte di esse hanno diverse dipendenze, istanziare classi richiede molto codice, quindi la possibilità di bug aumenta: codice che non esiste non può avere bug.
Negli ultimi anni sono apparse alcune librerie PHP che aiutano gli sviluppatori PHP a istanziare e salvare facilmente istanze di oggetti, risolvendo automaticamente le loro dipendenze.
Queste librerie sono conosciute come Dependency Injection Containers perché sono capaci di istanziare classi risolvendo le dipendenze e anche di salvare oggetti e restituirli quando servono, agendo similmente a un oggetto registry.
Di solito, quando si usano DI containers, gli sviluppatori devono impostare le dipendenze per ogni classe dell'applicazione, e poi la prima volta che una classe è necessaria nel codice essa è istanziata con le dipendenze appropriate e la stessa istanza è restituita ancora e ancora nelle richieste successive.
Alcuni DI containers sono anche capaci di scoprire automaticamente le dipendenze senza configurazione, ma usando PHP reflection.
Alcuni DI Containers ben conosciuti sono:
e molti altri.
Voglio sottolineare che per plugin semplici, che coinvolgono poche classi e classi con poche dipendenze, probabilmente non vale la pena usare DI containers: il metodo dell'istanza statica o un registry globalmente accessibile sono buone soluzioni, ma per plugin complessi i benefici di un DI container diventano evidenti.
Ovviamente, anche gli oggetti DI container devono essere accessibili per essere usati nell'applicazione e per quello scopo è possibile usare uno dei metodi visti sopra, variabile globale, variabile di istanza statica, restituire oggetto via filtro e così via.
Composer
Usare DI container spesso significa usare codice di terze parti. Oggigiorno, in PHP, quando abbiamo bisogno di usare una libreria esterna (quindi non solo DI containers, ma qualsiasi codice che non fa parte dell'applicazione), semplicemente scaricarla e metterla nella nostra cartella dell'applicazione non è considerata una buona pratica. Anche se siamo gli autori di quel pezzo di codice.
Disaccoppiare il codice di un'applicazione dalle dipendenze esterne è segno di migliore organizzazione, migliore affidabilità e migliore sanità del codice.
Composer, è lo standard de-facto nella comunità PHP per gestire le dipendenze PHP. Lontano dall'essere mainstream anche nella comunità WP, è uno strumento che ogni sviluppatore PHP e WordPress dovrebbe almeno conoscere, se non usare.
Questa risposta è già lunga quanto un libro per permettere ulteriori discussioni, e anche discutere di Composer qui è probabilmente fuori tema, è stato solo menzionato per completezza.
Per maggiori informazioni visita il sito di Composer e vale anche la pena leggere questo minisito curato da @Rarst.
1 PSR sono regole standard PHP rilasciate dal PHP Framework Interop Group
2 Composer (una libreria che sarà menzionata in questa risposta) tra le altre cose contiene anche un'utility per l'autoload.

Un'ulteriore nota, anche PHP 5.3 ha raggiunto la fine del suo ciclo di vita. Un hosting responsabile dovrebbe offrire almeno la 5.4 come opzione se non come default

"Dal 14 agosto 2014, PHP 5.3 ha raggiunto la fine del suo ciclo di vita. È definitivamente morto." È la prima riga sotto "PHP 5.2 è per zombie" @TomJNowell

Mi chiedevo solo, come sarebbe evidenziare un "sacco di cose"? ;-)

Nel tentativo di evitare variabili globali, mi piace l'idea del pattern Registry con funzione e variabile statica. La prima volta che la funzione viene chiamata istanzierà il registro, nelle chiamate successive lo restituirà semplicemente.

Il problema più grande con il registro è che questo crea un disordine di variabili e gli errori di battitura possono causare molti problemi. Se stai utilizzando classi predefinite, tutti gli IDE moderni ti forniranno una panoramica delle proprietà della classe ecc. e potrai facilmente scegliere quella corretta. Gli array sono una delle peggiori variabili in quanto causano molti problemi. L'errore più comune che vedo nei plugin di WordPress è "undefined index". L'uso delle classi assicura che tutte le tue proprietà siano definite quando ne hai bisogno. I registry portano a un'architettura più sporca e soggetta a errori.

Esistono plugin WordPress di grandi dimensioni che non utilizzano una classe "God" e che posso vedere su GitHub? Per quanto ne so, i grandi vendor come Skyverge e altri utilizzano ancora il concetto di classe God.

Buona domanda, ci sono diversi approcci e dipende da cosa vuoi ottenere.
Io spesso faccio così:
add_action( 'plugins_loaded', array( 'someClassy', 'init' ));
class someClassy {
public static function init() {
$class = __CLASS__;
new $class;
}
public function __construct() {
//costruisci qui ciò che ritieni opportuno...
}
//ecc...
}
Un esempio più approfondito e completo, nato da alcune recenti discussioni su questo stesso argomento nella chat room, può essere visto in questo gist del membro WPSE toscho.
L'approccio con costruttore vuoto.
Ecco un estratto dei vantaggi/svantaggi preso dal gist sopra citato che esemplifica pienamente l'approccio con costruttore vuoto.
Vantaggi:
I test unitari possono creare nuove istanze senza attivare automaticamente alcun hook. Niente Singleton.
Non è necessaria alcuna variabile globale.
Chiunque voglia lavorare con l'istanza del plugin può semplicemente chiamare T5_Plugin_Class_Demo::get_instance().
Facile da disattivare.
Ancora vero OOP: nessun metodo di lavoro è statico.
Svantaggio:
- Forse più difficile da leggere?
Lo svantaggio secondo me è piuttosto debole, motivo per cui deve essere il mio approccio preferito, anche se non l'unico che uso. Infatti, diversi altri esperti senza dubbio interverranno su questo argomento con il loro punto di vista a breve, perché ci sono alcune buone opinioni su questo tema che meritano di essere espresse.
nota: devo trovare l'esempio del gist di toscho che analizzava 3 o 4 confronti su come istanziare una classe in un plugin, esaminando i pro e i contro di ciascuno, dove il link sopra citato era il modo preferito per farlo, ma gli altri esempi forniscono un buon contrasto a questo argomento. Spero che toscho lo abbia ancora in archivio.
Nota: La risposta WPSE a questo argomento con esempi e confronti rilevanti. Anche la soluzione migliore per istanziare una classe in WordPress.
add_shortcode( 'baztag', array( My_Plugin::get_instance(), 'foo' ) );
class My_Plugin {
private $var = 'foo';
protected static $instance = NULL;
public static function get_instance() {
// crea un oggetto
NULL === self::$instance and self::$instance = new self;
return self::$instance; // restituisce l'oggetto
}
public function foo() {
return $this->var; // mai echo o print in uno shortcode!
}
}

qual sarebbe la differenza tra add_action('plugins_loaded',...); e add_action('load-plugins.php',...); L'esempio che ho preso utilizzava quest'ultimo

Non sono sicuro, ma considerando i nomi direi che l'hook plugins_loaded
è una coda che viene eseguita dopo il caricamento dei plugin, mentre l'hook load-plugins.php
aggancerebbe la coda effettiva di caricamento dei plugin. Praticamente non fa molta differenza, ma teoricamente potrebbe causare problemi se si chiamano metodi quando non tutti i plugin sono caricati. Ecco perché preferirei plugins_loaded
. Ancora una volta, sto solo interpretando i loro nomi.

Da quello che ho capito, load-plugins.php, anche se funziona, è associato al file core update.php
e non fa parte delle azioni predefinite solite su cui si dovrebbe fare affidamento quando si tratta della sequenza di eventi che si attivano durante l'inizializzazione, e per questo motivo preferisco usare quegli hook che si applicano, in questo caso plugins_loaded
. Questo è quello che spesso chiamo un rapido riassunto di ciò che accade quando Action Reference. La mia spiegazione non è completa nella sua interezza.

Stai cercando questa risposta?

@toscho Sì, è proprio quella. Ricordo che ne abbiamo parlato in chat ma anche sotto forma di Gist. Ma è la stessa cosa. Grazie!

Molto utile, e add_shortcode() sembra essere un modo molto più semplice per includere l'output di una parte specifica di quella classe, piuttosto che recuperare un'istanza per così dire. È possibile utilizzare uno shortcode sia nei file template che nell'editor di WordPress?

@kalpaitch Se uno shortcode si adatta al tuo caso d'uso, allora sì, echo do_shortcode('[my-shortcode]');
nella sua forma più basilare, per quando vuoi chiamare uno shortcode al di fuori del tradizionale schermo dell'editor di post, farà al caso tuo.

Mi piace questo approccio simile a un singleton. Tuttavia, metto in discussione l'uso di plugins_loaded come tuo action hook di inizializzazione. Questo hook è pensato per essere eseguito dopo che tutti i plugin sono stati caricati. Agganciandoti dopo di esso, stai in un certo senso dirottando quell'hook e potresti incorrere in conflitti o problemi di sequenza di avvio con altri plugin o temi che si agganciano a plugins_loaded. Non mi aggancerei a nessuna action per eseguire il tuo metodo di inizializzazione. L'architettura dei plugin è stata progettata per essere eseguita in linea, non su un'action.

Nella maggior parte dei casi mi aggancio a init
, ma capisco il tuo punto riguardo a plugins_loaded
, ha perfettamente senso. Ha anche senso se la tua intenzione era di dirottare quell'hook per quello scopo. Non vedo l'uso degli hook allo stesso modo per i plugin come fai tu, ma sarei interessato a saperne di più sulla tua posizione sull'argomento se hai qualche lettura consigliata? Link? Ulteriori esempi di quali tipi di problemi di sequenza potremmo incontrare? Grazie amico..

@Tom Auger, capisco perfettamente il tuo punto riguardo al caricamento usando l'hook plugins_loaded. Sarebbe meglio utilizzare il primo metodo nella domanda per avviare una classe, o se proponi un metodo alternativo, potresti per favore includerlo in una risposta?

Nota che se usi register_activation_hook()
devi chiamare quella funzione prima che l'azione plugins_loaded
sia stata attivata.

@Geert, concordo, ho scoperto che questi hook di attivazione dovevano comunque essere inseriti in __construct

Come informazione aggiuntiva, consulta questo post di @mikeschinkel e la discussione nei commenti. http://hardcorewp.com/2012/using-classes-as-code-wrappers-for-wordpress-plugins/#comment-149

Utilizzo la seguente struttura:
Prefix_Example_Plugin::on_load();
/**
* Esempio di caricamento iniziale del plugin basato su classi.
*/
class Prefix_Example_Plugin {
/**
* Aggancia l'init (nient'altro) e chiama le cose che devono essere eseguite subito.
*/
static function on_load() {
// se necessario, qui va l'interruttore di disattivazione (se è definita la costante di disabilitazione, allora esci)
add_action( 'init', array( __CLASS__, 'init' ) );
}
/**
* Configurazione ulteriore degli hook, caricamento dei file, ecc.
*
* Nota che per i metodi agganciati il nome corrisponde all'hook (quando possibile).
*/
static function init( ) {
}
}
Note:
- ha un posto definito per le cose che devono essere eseguite subito
- la disattivazione/sovrascrittura per modifiche è semplice (sgancia un metodo
init
) - Non credo di aver mai usato/avuto bisogno di un oggetto della classe del plugin - richiede di tenerne traccia, ecc; questo è in realtà un finto namespace per scopo, non OOP (la maggior parte delle volte)
Disclaimer Non uso ancora i test unitari (così tante cose sul mio piatto) e ho sentito che il static può essere meno preferibile per loro. Fai le tue ricerche su questo se hai bisogno di testarlo.

So che le persone che sono molto attente ai test unitari non amano particolarmente le soluzioni statiche/singleton. Penso che se si comprende appieno ciò che si sta cercando di ottenere utilizzando metodi statici e si è almeno consapevoli delle implicazioni del loro utilizzo, allora è perfettamente accettabile implementare tali metodi. Ci sono ottime discussioni su questo argomento su [SO]

Questo mi ha fatto riflettere seriamente. Allora perché usare Classi e non tornare semplicemente a funzioni con prefisso? Lo facciamo solo per avere nomi di funzioni/metodi più puliti? Voglio dire, averle annidate con un "static" davanti le rende davvero più leggibili? La possibilità di avere un conflitto di nomi è circa la stessa che per un singolo nome di classe se si usano prefissi appropriati, o mi sfugge qualcosa?

@JamesMitch sì, metodi completamente statici sono in pratica solo funzioni con finti namespace come usato in WP. Tuttavia le classi hanno alcuni vantaggi rispetto alle pure funzioni anche in questo caso, come l'autoload e l'ereditarietà. Ultimamente mi sto spostando dai metodi statici verso veri oggetti istanziati organizzati da un contenitore per l'iniezione delle dipendenze.

Ottima idea, ma può essere utilizzata solo sulla classe principale del plugin. Uno dei problemi più grandi di molti plugin WordPress è che caricano tonnellate di codice che non viene mai utilizzato per la richiesta. Per evitare di caricare tonnellate di codice che in realtà non viene usato, devi suddividere la tua funzionalità in parti più piccole utilizzando namespace e classi e poi usare l'autoload, che carica solo questi file, che sono effettivamente utilizzati. Questo tipo di approccio richiede uno sviluppo più attento del plugin, per assicurarsi di avere una logica basata sulla richiesta (il codice viene usato sempre per una richiesta e poi termina).

Tutto dipende dalla funzionalità.
Una volta ho creato un plugin che registrava gli script quando veniva chiamato il costruttore, quindi ho dovuto agganciarlo all'hook wp_enqueue_scripts
.
Se vuoi chiamarlo quando il tuo file functions.php
viene caricato, potresti anche creare un'istanza manualmente $class_instance = new ClassName();
come hai menzionato.
Potresti voler considerare la velocità e l'utilizzo della memoria. Non sono a conoscenza di nessun caso specifico, ma posso immaginare che ci siano hook non chiamati in alcune circostanze. Creando la tua istanza a quell'hook potresti risparmiare alcune risorse del server.

Bene, grazie per questo, suppongo ci siano due punti nella domanda precedente. L'altro è se __construct è adatto o se init() è un modo migliore per inizializzare la classe.

So che questo è un po' datato, ma nel frattempo PHP 5.3 supporta i metodi anonimi, quindi ho pensato a questo:
add_action( 'plugins_loaded', function() { new My_Plugin(); } );
E in qualche modo è la soluzione che preferisco. Posso usare costruttori regolari e non ho bisogno di definire alcun metodo "init" o "on_load" che incasinino le mie strutture OOP.
