Cea mai bună modalitate de a iniția o clasă într-un plugin WordPress

22 oct. 2012, 12:22:32
Vizualizări: 76.9K
Voturi: 108

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

Referitor la ultima editare, dacă acest lucru este conținut în același fișier de plugin ca și clasa, devine oarecum inutil. La fel de bine poți instanția clasa conform metodei pe care o descriu. Chiar dacă este într-un fișier separat, tot este cam fără rost. Singurul caz de utilizare pe care îl văd este dacă dorești să creezi o funcție wrapper care să-ți permită să instanțiezi o clasă în afara fișierelor pluginului tău, în cadrul temelor și așa mai departe. Chiar și așa, aș fi nevoit să întreb ce logică stă în spatele acestei decizii, deoarece utilizarea corectă a condiționalelor și a hook-urilor ar trebui să permită un control fin asupra instanțierii, permițându-ți să te concentrezi pe utilizarea pluginului.

Adam Adam
23 oct. 2012 19:14:39

Sunt oarecum de acord cu asta, dar am considerat că merită menționat, deoarece l-am găsit în câteva plugin-uri WP.

kalpaitch kalpaitch
24 oct. 2012 11:40:37
Toate răspunsurile la întrebare 5
7
92

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

27 oct. 2014 00:48:49
Comentarii

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

Tom J Nowell Tom J Nowell
27 oct. 2014 12:55:54

"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

gmazzap gmazzap
27 oct. 2014 13:50:49

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

MikeSchinkel MikeSchinkel
25 dec. 2015 11:15:58

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

Michael Ecklund Michael Ecklund
11 aug. 2017 19:00:43

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.

D.A.H D.A.H
20 mai 2021 11:47:31

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

Kevin Chavez Kevin Chavez
10 aug. 2021 09:04:22

Există actualizări pentru acest lucru în 2022? De asemenea, când / unde este apelată funcția setup() pentru clasele dumneavoastră?

Kevin Chavez Kevin Chavez
10 ian. 2022 04:22:43
Arată celelalte 2 comentarii
14
71

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.

Abordarea cu constructor gol.

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!
    }
}
22 oct. 2012 12:42:51
Comentarii

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

kalpaitch kalpaitch
22 oct. 2012 12:46:42

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.

Tim S. Tim S.
22 oct. 2012 13:00:16

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.

Adam Adam
22 oct. 2012 13:00:28

Cauți acest răspuns?

fuxia fuxia
22 oct. 2012 16:02:13

@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!

Adam Adam
22 oct. 2012 16:37:25

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 kalpaitch
22 oct. 2012 18:47:22

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

Adam Adam
22 oct. 2012 18:59:45

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

Tom Auger Tom Auger
24 oct. 2012 00:25:56

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.

Adam Adam
24 oct. 2012 00:46:22

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

kalpaitch kalpaitch
24 oct. 2012 11:38:07

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 Geert
31 oct. 2012 11:52:33

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

kalpaitch kalpaitch
2 nov. 2012 17:29:56

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

bueltge bueltge
3 ian. 2013 13:24:59

@bueltge Mulțumesc pentru link, o să citesc. Pare o discuție interesantă!

Adam Adam
3 ian. 2013 13:53:39
Arată celelalte 9 comentarii
4
11

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.

22 oct. 2012 13:10:19
Comentarii

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

Adam Adam
22 oct. 2012 13:21:34

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?

James Mitch James Mitch
28 mai 2013 08:52:37

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

Rarst Rarst
28 mai 2013 13:35:23

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

D.A.H D.A.H
20 mai 2021 11:21:55
2

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.

22 oct. 2012 12:28:35
Comentarii

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.

kalpaitch kalpaitch
22 oct. 2012 12:40:28

Eu aș opta pentru o metodă statică init() astfel încât instanța clasei să fie apelată în scopul clasei, nu într-un alt scop unde ai putea suprascrie accidental variabile existente.

Tim S. Tim S.
22 oct. 2012 12:54:10
1

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

25 sept. 2017 20:56:58
Comentarii

Puteți utiliza atât funcții anonime, cât și clase anonime. Deci puteți folosi clase anonime dacă clasa dvs. are sarcini limitate și nu trebuie să o serializați.

D.A.H D.A.H
20 mai 2021 11:24:50