Cea mai bună modalitate de a iniția o clasă într-un plugin WordPress
Am creat un plugin și, bineînțeles, am vrut să folosesc o abordare orientată pe obiecte (OO). Până acum, am creat această clasă și imediat sub ea am creat o instanță a acestei clase:
class ClassName {
public function __construct(){
}
}
$class_instance = new ClassName();
Presupun că există o modalitate mai specifică WordPress pentru a iniția această clasă, și apoi am dat peste persoane care spun că preferă să aibă o funcție init()
în loc de una __construct()
. În mod similar, am găsit câțiva oameni care folosesc următorul hook:
class ClassName {
public function init(){
}
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );
Care este considerată, în general, cea mai bună modalitate de a crea o instanță de clasă WordPress la încărcare și de a o avea ca variabilă accesibilă global?
NOTĂ: Ca un punct interesant, am observat că în timp ce register_activation_hook()
poate fi apelat din __construct
, nu poate fi apelat din init()
folosind al doilea exemplu. Poate cineva să mă lămurească în această privință.
Editare: Mulțumesc pentru toate răspunsurile, există în mod clar destule dezbateri despre cum să gestionezi inițializarea în interiorul clasei în sine, dar cred că există un consens destul de bun că add_action( 'plugins_loaded', ...);
este cea mai bună modalitate de a o porni...
Editare: Doar pentru a complica lucrurile, am văzut și această metodă folosită (deși eu personal nu aș folosi această metodă deoarece transformarea unei clase OO într-o funcție pare să contrazică scopul acesteia):
// Pornește acest plugin
add_action( 'init', 'ClassName' );
function ClassName() {
global $class_name;
$class_name = new ClassName();
}

Sosind aici exact la 2 ani după ce a fost pusă întrebarea originală, există câteva lucruri pe care vreau să le subliniez. (Nu mă rugați să subliniez multe lucruri, niciodată).
Hook-ul potrivit
Pentru a instanția o clasă de plugin, ar trebui folosit hook-ul corespunzător. Nu există o regulă generală pentru care ar fi acesta, deoarece depinde de ceea ce face clasa.
Folosirea unui hook foarte devreme precum "plugins_loaded"
adesea nu are sens, deoarece un astfel de hook este declanșat pentru cererile de administrare, frontend și AJAX, dar foarte des un hook mai târziu este mult mai bun deoarece permite instanțierea claselor de plugin doar atunci când este necesar.
De exemplu, o clasă care face lucruri pentru șabloane poate fi instanțiată pe "template_redirect"
.
În general, este foarte rar ca o clasă să aibă nevoie să fie instanțiată înainte ca "wp_loaded"
să fie declanșat.
Fără "God Class"
Majoritatea claselor folosite ca exemple în răspunsurile mai vechi folosesc o clasă numită precum "Prefix_Example_Plugin"
sau "My_Plugin"
... Aceasta indică faptul că probabil există o clasă principală pentru plugin.
Ei bine, cu excepția cazului în care un plugin este format dintr-o singură clasă (în acest caz, denumirea după numele pluginului este absolut rezonabilă), crearea unei clase care gestionează întregul plugin (de exemplu, adăugarea tuturor hook-urilor de care are nevoie un plugin sau instanțierea tuturor celorlalte clase de plugin) poate fi considerată o practică proastă, ca un exemplu de god object.
În programarea orientată pe obiecte, codul ar trebui să tindă să fie S.O.L.I.D. unde "S" înseamnă "Principiul responsabilității unice".
Aceasta înseamnă că fiecare clasă ar trebui să facă un singur lucru. În dezvoltarea de plugin-uri WordPress, înseamnă că dezvoltatorii ar trebui să evite să folosească un singur hook pentru a instanția o clasă principală de plugin, ci diferite hook-uri ar trebui folosite pentru a instanția diferite clase, în funcție de responsabilitatea clasei.
Evitați hook-urile în constructor
Acest argument a fost introdus în alte răspunsuri de aici, totuși vreau să subliniez acest concept și să leg acest alt răspuns unde a fost explicat destul de amplu în contextul testării unitare.
Aproape 2015: PHP 5.2 este pentru zombi
Din 14 august 2014, PHP 5.3 a ajuns la sfârșitul vieții. Este cu siguranță mort. PHP 5.4 va fi suportat pe tot parcursul anului 2015, ceea ce înseamnă încă un an în momentul în care scriu.
Cu toate acestea, WordPress încă suportă PHP 5.2, dar nimeni nu ar trebui să scrie o singură linie de cod care să suporte acea versiune, mai ales dacă codul este OOP.
Există diferite motive:
- PHP 5.2 a murit de mult timp, nu se lansează actualizări de securitate pentru el, ceea ce înseamnă că nu este sigur
- PHP 5.3 a adăugat multe caracteristici PHP, funcții anonime și namespace-uri über alles
- versiunile mai noi de PHP sunt mult mai rapide. PHP este gratuit. Actualizarea lui este gratuită. De ce să folosești o versiune mai lentă, nesigură, dacă poți folosi una mai rapidă, mai sigură, gratuit?
Dacă nu doriți să folosiți cod PHP 5.4+, folosiți cel puțin 5.3+
Exemplu
În acest moment este timpul să revizuim răspunsurile mai vechi pe baza a ceea ce am spus până acum.
Odată ce nu mai trebuie să ne pese de 5.2, putem și ar trebui să folosim namespace-uri.
Pentru a explica mai bine principiul responsabilității unice, exemplul meu va folosi 3 clase, una care face ceva pe frontend, una pe backend și o a treia folosită în ambele cazuri.
Clasa Admin:
namespace GM\WPSE\Example;
class AdminStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Clasa Frontend:
namespace GM\WPSE\Example;
class FrontStuff {
private $tools;
function __construct( ToolsInterface $tools ) {
$this->tools = $tools;
}
function setup() {
// setup class, maybe add hooks
}
}
Interfața Tools:
namespace GM\WPSE\Example;
interface ToolsInterface {
function doSomething();
}
Și o clasă Tools, folosită de celelalte două:
namespace GM\WPSE\Example;
class Tools implements ToolsInterface {
function doSomething() {
return 'done';
}
}
Având aceste clase, le pot instanția folosind hook-uri potrivite. Ceva de genul:
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; // aceasta nu este ideal, motivul este explicat mai jos
$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; // aceasta nu este ideal, motivul este explicat mai jos
$front_stuff = new GM\WPSE\Example\FrontStuff( $tools );
} );
Inversarea dependențelor & Injectarea dependențelor
În exemplul de mai sus am folosit namespace-uri și funcții anonime pentru a instanția diferite clase la diferite hook-uri, punând în practică ceea ce am spus mai sus.
Observați cum namespace-urile permit crearea de clase denumite fără niciun prefix.
Am aplicat un alt concept care a fost menționat indirect mai sus: Injectarea dependențelor, este o metodă de a aplica Principiul inversării dependențelor, "D" din acronimul SOLID.
Clasa Tools
este "injectată" în celelalte două clase atunci când acestea sunt instanțiate, astfel încât în acest fel este posibilă separarea responsabilităților.
În plus, clasele AdminStuff
și FrontStuff
folosesc type hinting pentru a declara că au nevoie de o clasă care implementează ToolsInterface
.
În acest fel, noi sau utilizatorii care folosesc codul nostru pot folosi diferite implementări ale aceleiași interfețe, făcând codul nostru să nu fie cuplat la o clasă concretă, ci la o abstracție: exact despre asta este vorba în Principiul inversării dependențelor.
Cu toate acestea, exemplul de mai sus poate fi îmbunătățit în continuare. Să vedem cum.
Autoloader
O modalitate bună de a scrie cod OOP mai ușor de citit este să nu amestecăm definițiile de tipuri (Interfețe, Clase) cu alt cod și să punem fiecare tip în propriul fișier.
Această regulă este, de asemenea, una dintre standardele de codare PSR-11.
Cu toate acestea, făcând acest lucru, înainte de a putea folosi o clasă, trebuie să încărcați fișierul care o conține.
Acest lucru poate fi copleșitor, dar PHP oferă funcții utilitare pentru a încărca automat o clasă atunci când este necesară, folosind un callback care încarcă un fișier pe baza numelui său.
Folosind namespace-uri, devine foarte ușor, deoarece acum este posibil să potriviți structura folderelor cu structura namespace-urilor.
Aceasta nu este doar posibilă, ci este și un alt standard PSR (sau mai bine 2: PSR-0 acum învechit și PSR-4).
Urmând aceste standarde, este posibil să folosiți diferite unelte care gestionează încărcarea automată, fără a fi nevoie să codificați un autoloader personalizat.
Trebuie să spun că standardele de codare WordPress au reguli diferite pentru denumirea fișierelor.
Deci, atunci când scrieți cod pentru nucleul WordPress, dezvoltatorii trebuie să urmeze regulile WP, dar atunci când scrieți cod personalizat, este o alegere a dezvoltatorului, dar utilizarea standardului PSR este mai ușoară pentru a folosi unelte deja scrise2.
Acces global, Registry și modelele Service Locator.
Una dintre cele mai mari probleme atunci când instanțiați clase de plugin în WordPress este cum să le accesați din diferite părți ale codului.
WordPress în sine folosește abordarea globală: variabilele sunt salvate în domeniul global, făcându-le accesibile peste tot. Fiecare dezvoltator WordPress tastează cuvântul global
de mii de ori în cariera lor.
Aceasta este și abordarea pe care am folosit-o pentru exemplul de mai sus, dar este rea.
Acest răspuns este deja mult prea lung pentru a-mi permite să explic în continuare de ce, dar citirea primelor rezultate din SERP pentru "variabile globale rele" este un bun punct de plecare.
Dar cum este posibil să evităm variabilele globale?
Există diferite modalități.
Unele dintre răspunsurile mai vechi de aici folosesc abordarea instanței statice.
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
Este ușor și destul de bine, dar forțează implementarea modelului pentru fiecare clasă pe care dorim să o accesăm.
Mai mult, de multe ori această abordare pune la îndemână să cadă în problema clasei god, deoarece dezvoltatorii fac accesibilă o clasă principală folosind această metodă și apoi o folosesc pentru a accesa toate celelalte clase.
Am explicat deja cât de rea este o clasă god, așa că abordarea instanței statice este o modalitate bună de a merge atunci când un plugin are nevoie doar să facă accesibilă una sau două clase.
Acest lucru nu înseamnă că poate fi folosit doar pentru plugin-uri care au doar câteva clase, de fapt, atunci când principiul injectării dependențelor este utilizat corespunzător, este posibil să creați aplicații destul de complexe fără a fi nevoie să faceți accesibile global un număr mare de obiecte.
Cu toate acestea, uneori plugin-urile trebuie să facă accesibile unele clase, iar în acest caz abordarea instanței statice este copleșitoare.
O altă abordare posibilă este utilizarea modelului registry.
Aceasta este o implementare foarte simplă a acestuia:
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;
}
}
Folosind această clasă, este posibil să stocați obiecte în obiectul registry după un id, astfel încât având acces la un registry, să aveți acces la toate obiectele. Desigur, atunci când un obiect este creat pentru prima dată, trebuie adăugat la registry.
Exemplu:
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' ) );
Exemplul de mai sus arată clar că pentru a fi util, registry-ul trebuie să fie accesibil global. O variabilă globală doar pentru registry nu este foarte rea, totuși pentru non-global puriști este posibil să implementați abordarea instanței statice pentru un registry sau poate o funcție cu o variabilă statică:
function gm_wpse_example_registry() {
static $registry = NULL;
if ( is_null( $registry ) ) {
$registry = new GM\WPSE\Example\Registry;
}
return $registry;
}
Prima dată când funcția este apelată, va instanția registry-ul, la apelurile ulterioare va doar returna-l.
O altă metodă specifică WordPress pentru a face o clasă accesibilă global este returnarea unei instanțe de obiect dintr-un filtru. Ceva de genul:
$registry = new GM\WPSE\Example\Registry;
add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
return $registry;
} );
După aceea, oriunde este nevoie de registry:
$registry = apply_filters( 'gm_wpse_example_registry', NULL );
Un alt model care poate fi utilizat este modelul service locator. Este similar cu modelul registry, dar service locator-ii sunt pasați la diverse clase folosind injectarea dependențelor.
Principala problemă cu acest model este că ascunde dependențele claselor, făcând codul mai greu de întreținut și citit.
Containere DI
Indiferent de metoda folosită pentru a face registry sau service locator accesibil global, obiectele trebuie stocate acolo, iar înainte de a fi stocate, trebuie instanțiate.
În aplicații complexe, unde există destul de multe clase și multe dintre ele au mai multe dependențe, instanțierea claselor necesită mult cod, astfel încât posibilitatea de bug-uri crește: codul care nu există nu poate avea bug-uri.
În ultimii ani au apărut unele biblioteci PHP care ajută dezvoltatorii PHP să instanțieze și să stocheze cu ușurință instanțe de obiecte, rezolvând automat dependențele lor.
Aceste biblioteci sunt cunoscute sub numele de Containere de Injectare a Dependențelor deoarece sunt capabile să instanțieze clase rezolvând dependențele și, de asemenea, să stocheze obiecte și să le returneze atunci când este nevoie, acționând similar cu un obiect registry.
De obicei, atunci când se utilizează containere DI, dezvoltatorii trebuie să configureze dependențele pentru fiecare clasă a aplicației, iar apoi prima dată când o clasă este necesară în cod, aceasta este instanțiată cu dependențele potrivite și aceeași instanță este returnată din nou și din nou la solicitările ulterioare.
Unele containere DI sunt, de asemenea, capabile să descopere automat dependențele fără configurare, dar folosind reflecția PHP.
Unele containere DI bine cunoscute sunt:
și multe altele.
Vreau să subliniez că pentru plugin-uri simple, care implică doar câteva clase și clasele nu au multe dependențe, probabil nu merită să folosiți containere DI: metoda instanței statice sau un registry accesibil global sunt soluții bune, dar pentru plugin-uri complexe beneficiile unui container DI devin evidente.
Desigur, chiar și obiectele container DI trebuie să fie accesibile pentru a fi utilizate în aplicație și în acest scop este posibil să folosiți una dintre metodele văzute mai sus, variabilă globală, variabilă statică de instanță, returnarea obiectului prin filtru și așa mai departe.
Composer
Folosirea unui container DI adesea înseamnă utilizarea codului terț. În zilele noastre, în PHP, atunci când avem nevoie să folosim o bibliotecă externă (deci nu doar containere DI, ci orice cod care nu face parte din aplicație), simpla descărcare și plasare în folderul aplicației nu este considerată o practică bună. Chiar dacă suntem autorii acelei alte bucăți de cod.
Decuplarea codului aplicației de dependențele externe este un semn de organizare mai bună, fiabilitate mai bună și sănătate mai bună a codului.
Composer, este standardul de facto în comunitatea PHP pentru gestionarea dependențelor PHP. Departe de a fi mainstream și în comunitatea WP, este un instrument pe care fiecare dezvoltator PHP și WordPress ar trebui să-l cunoască cel puțin, dacă nu să-l folosească.
Acest răspuns este deja de dimensiunea unei cărți pentru a permite o discuție suplimentară și, de asemenea, discuția despre Composer aici este probabil off topic, a fost menționat doar pentru acuratețe.
Pentru mai multe informații, vizitați site-ul Composer și merită să citiți și acest minisite realizat de @Rarst.
1 PSR sunt reguli standard PHP lansate de PHP Framework Interop Group
2 Composer (o bibliotecă care va fi menționată în acest răspuns) printre altele conține și un utilitar de încărcare automată.

O notă suplimentară, PHP 5.3 este de asemenea în afara perioadei de suport. Un furnizor de hosting responsabil va oferi cel puțin versiunea 5.4 ca opțiune, dacă nu chiar implicită.

"Din 14 august 2014, PHP 5.3 a ieșit din perioada de suport. Este cu siguranță mort." Este prima linie sub secțiunea "PHP 5.2 este pentru zombi" @TomJNowell

Doar mă întrebam, cum ar arăta să evidențiezi "o mulțime de lucruri"? ;-)

În încercarea de a evita variabilele globale, îmi place ideea modelului Registry cu funcție și variabilă statică. Prima dată când funcția este apelată, va instanția registrul, iar la apelurile ulterioare va doar returna acesta.

Cea mai mare problemă cu registrul este că acesta creează o mizerie de variabile, iar greșelile de scriere pot provoca multe probleme. Dacă utilizați clase predefinite, toate IDE-urile moderne vă vor oferi o privire de ansamblu asupra proprietăților clasei etc. și puteți alege cu ușurință pe cea corectă. Array-urile sunt unele dintre cele mai proaste variabile, deoarece provoacă multe probleme. Cea mai frecventă eroare pe care o văd în plugin-urile WordPress este "undefined index". Utilizarea claselor asigură că toate proprietățile dvs. sunt definite ori de câte ori aveți nevoie de ele. Registrele duc la o arhitectură mai murdară și plină de erori.

Există plugin-uri WordPress de dimensiuni mai mari care nu folosesc o clasă "God" pe care să le pot vedea pe github? Din câte pot spune, furnizorii mai mari precum Skyverge și alții încă folosesc ideea de clasă "God".

Bună întrebare, există mai multe abordări și depinde de ceea ce dorești să obții.
Eu folosesc adesea:
add_action( 'plugins_loaded', array( 'someClassy', 'init' ));
class someClassy {
public static function init() {
$class = __CLASS__;
new $class;
}
public function __construct() {
//aici poți construi ceea ce consideri necesar...
}
//etc...
}
Un exemplu mai amplu și detaliat, care a apărut ca urmare a unor discuții recente pe această temă în cadrul camerei de chat, poate fi văzut în acest gist de către membru WPSE toscho.
Iată un fragment cu avantaje/dezavantaje preluat din gist-ul menționat mai sus, care exemplifică pe deplin abordarea cu constructor gol.
Avantaje:
Testele unitare pot crea instanțe noi fără a activa automat niciun hook. Fără Singleton.
Nu este necesară nicio variabilă globală.
Oricine dorește să lucreze cu instanța pluginului poate pur și simplu să apeleze T5_Plugin_Class_Demo::get_instance().
Ușor de dezactivat.
Totuși OOP adevărat: niciuna dintre metodele de lucru nu este statică.
Dezavantaj:
- Poate mai greu de citit?
În opinia mea, dezavantajul este unul minor, motiv pentru care ar trebui să fie abordarea mea preferată, dar nu singura pe care o folosesc. De fapt, fără îndoială, și alți experți vor avea opinii valoroase pe această temă, care merită exprimate.
notă: Trebuie să găsesc exemplul din gist-ul lui toscho care prezenta 3 sau 4 comparații despre cum să instantiezi o clasă într-un plugin, analizând pro și contra fiecăreia, iar link-ul de mai sus reprezenta metoda preferată. Sper ca toscho să mai aibă acel exemplu.
Notă: Răspunsul WPSE la această temă cu exemple relevante și comparații. De asemenea, cea mai bună soluție pentru instanțierea unei clase în 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() {
// creează un obiect
NULL === self::$instance and self::$instance = new self;
return self::$instance; // returnează obiectul
}
public function foo() {
return $this->var; // niciodată să nu folosești echo sau print într-un shortcode!
}
}

care ar fi diferența dintre add_action('plugins_loaded',...); și add_action('load-plugins.php',...); Exemplul pe care l-am luat a folosit pe ultimul

Nu sunt sigur, dar având în vedere numele, aș spune că hook-ul plugins_loaded
este o coadă care rulează după ce plugin-urile sunt încărcate, în timp ce hook-ul load-plugins.php
ar fi legat de coada reală de încărcare a plugin-urilor. Practic, nu prea există o diferență mare, dar teoretic ar putea cauza probleme dacă apelezi metode când nu toate plugin-urile sunt încărcate. De aceea aș prefera plugins_loaded
. Din nou, doar interpretez numele lor.

Din ce înțeleg, load-plugins.php, deși funcționează, este asociat cu fișierul de bază update.php
și nu face parte din acțiunile implicite obișnuite pe care ar trebui să ne bazăm când vine vorba de secvența de evenimente care se declanșează în timpul inițializării. Din acest motiv, prefer să folosesc acele hook-uri care se aplică, în acest caz plugins_loaded
. Aceasta este ceea ce deseori numesc o imagine rapidă a ceea ce se întâmplă când Referință Acțiuni. Explicația mea nu este completă în întregime.

Cauți acest răspuns?

@toscho Da, exact acela. Îmi amintesc că ne uitam la asta în chat dar sub forma unui Gist de asemenea. Dar e una și aceeași. Mulțumesc pentru asta!

Foarte util, iar add_shortcode() pare a fi o metodă mult mai simplă de a include rezultatul unei anumite părți din acea clasă, decât să obții o instanță per se. Este posibil să folosești un shortcode atât în fișierele de template cât și în Editorul WordPress?

@kalpaitch Dacă un shortcode se potrivește cu cazul tău de utilizare, atunci da, echo do_shortcode('[my-shortcode]');
în forma sa cea mai simplă, pentru când vrei să apelezi un shortcode în afara ecranului tradițional al editorului de postări, va funcționa.

Îmi place această abordare asemănătoare unui singleton. Totuși, mă îndoiesc de utilizarea lui plugins_loaded ca și action hook de inițializare. Acest hook este menit să ruleze după ce toate plugin-urile s-au încărcat. Prin conectarea după el, cam deturnezi acel hook și poți întâmpina conflicte sau probleme de secvență de pornire cu alte plugin-uri sau teme care se conectează la plugins_loaded. Nu m-aș conecta la nicio acțiune pentru a rula metoda ta de inițializare. Arhitectura plugin-ului a fost proiectată să ruleze inline, nu pe o acțiune.

Mă conectez de obicei la init
, dar înțeleg punctul tău legat de plugins_loaded
, are sens perfect. De asemenea, are sens dacă intenția ta a fost să deturnezi acel hook în acest scop. Nu văd utilizarea hook-urilor în același mod pentru plugin-uri ca tine, dar aș fi interesat să învăț mai multe despre poziția ta pe această temă dacă ai niște lecturi recomandate? Link-uri? Exemple suplimentare despre ce fel de probleme de secvență am putea întâlni? Mulțumesc, prietene.

@Tom Auger, înțeleg perfect punctul tău de vedere referitor la încărcarea folosind hook-ul plugins_loaded. Ar fi mai bine să folosești prima metodă din întrebare pentru inițializarea unei clase, sau dacă propui o metodă alternativă, poți să incluzi și un răspuns?

Reține că dacă folosești register_activation_hook()
, trebuie să apelezi acea funcție înainte ca acțiunea plugins_loaded
să fie declanșată.

@Geert, de acord, am descoperit că aceste hook-uri de activare tot trebuiau plasate în __contruct

Ca informație suplimentară, consultă acest post de la @mikeschinkel și discuția din comentarii. http://hardcorewp.com/2012/using-classes-as-code-wrappers-for-wordpress-plugins/#comment-149

Folosesc următoarea structură:
Prefix_Example_Plugin::on_load();
/**
* Exemplu de încărcare inițială a unui plugin bazat pe clase.
*/
class Prefix_Example_Plugin {
/**
* Inițializează cârlige (doar init) și apelează lucrurile care trebuie să ruleze imediat.
*/
static function on_load() {
// dacă este necesar, aici se poate adăuga un mecanism de oprire (dacă este definită o constantă de dezactivare, atunci return)
add_action( 'init', array( __CLASS__, 'init' ) );
}
/**
* Configurarea suplimentară a cârligelor, încărcarea fișierelor etc.
*
* Rețineți că pentru metodele cârlige, numele este egal cu cârligul (atunci când este posibil).
*/
static function init( ) {
}
}
Observații:
- are un loc definit pentru lucrurile care trebuie să ruleze imediat
- dezactivarea/înlocuirea pentru ajustări este ușoară (deconectarea unei singure metode
init
) - Nu cred că am folosit/nevoit vreodată un obiect al clasei pluginului - necesită urmărirea acestuia, etc.; aceasta este de fapt o falsă spațializare intenționată, nu OOP (de cele mai multe ori)
Avertisment Încă nu folosesc teste unitare (atât de multe lucruri pe lista mea) și am auzit că static poate fi mai puțin preferabil pentru ele. Fă-ți propria cercetare pe acest subiect dacă ai nevoie să testezi unitar.

Știu că oamenii pasionați de testarea unitară nu prea sunt încântați de soluțiile statice/singleton. Cred că dacă înțelegi pe deplin ce încerci să obții folosind metode statice și ești măcar conștient de implicațiile acestui lucru, atunci e perfect în regulă să implementezi astfel de metode. Există discuții interesante pe această temă pe [SO]

Asta m-a făcut să reflectez. Deci de ce să folosim Clase și nu să ne întoarcem la simple funcții prefixate? Oare facem asta doar pentru a avea nume de funcții/metode mai curate? Adică să le avem imbricate cu un "static" în față e chiar atât de mult mai lizibil? Șansa de a avea un conflict de nume e cam la fel ca pentru un singur nume de clasă dacă folosești prefixe corecte sau eu ratez ceva?

@JamesMitch da, metodele complet statice sunt în mare parte doar funcții cu namespace fals așa cum se folosește în WP. Totuși, clasele au câteva avantaje față de funcțiile pure chiar și în acest caz, cum ar fi autoload-ul și moștenirea. În ultima vreme am trecut de la metode statice către obiecte instantiate organizate prin container de dependency injection.

Idee interesantă, dar poate fi folosită doar pe clasa principală a plugin-ului. Una dintre cele mai mari probleme ale multor plugin-uri WordPress este că încarcă o tonă de cod care nu este niciodată folosit pentru cerere. Pentru a evita încărcarea unei mulțimi de cod nefolosit, trebuie să împărțiți funcționalitatea în bucăți mai mici folosind namespace-uri și clase, apoi să utilizați autoload, care încarcă doar acele fișiere care sunt de fapt folosite. Acest tip de abordare necesită o dezvoltare mai atentă a plugin-ului, pentru a vă asigura că aveți o logică bazată pe cerere (codul este folosit întotdeauna pentru o singură cerere și moare după aceea).

Totul depinde de funcționalitate.
Am creat odată un plugin care înregistra scripturi când constructorul era apelat, așa că a trebuit să-l conectez la hook-ul wp_enqueue_scripts
.
Dacă dorești să-l apelezi când fișierul tău functions.php
este încărcat, poți la fel de bine să creezi o instanță manual $class_instance = new ClassName();
cum ai menționat.
Ar fi bine să iei în considerare viteza și utilizarea memoriei. Nu cunosc cazuri specifice, dar îmi pot imagina că există hook-uri neapelate în anumite situații. Prin crearea instanței tale la acel hook, ai putea economisi resurse de server.

Super, mulțumesc pentru asta, presupun că există două aspecte în întrebarea de mai sus. Celălalt fiind dacă __construct este potrivit sau dacă init() este o modalitate mai bună de a inițializa clasa.

Știu că asta are câțiva ani, dar între timp PHP 5.3 suportă metode anonime, așa că am venit cu această soluție:
add_action( 'plugins_loaded', function() { new My_Plugin(); } );
Și cumva, aceasta îmi place cel mai mult. Pot folosi constructori obișnuiți și nu trebuie să definesc niciun fel de metode "init" sau "on_load" care încurcă structurile mele OOP.
