Decodarea clasei proxy în OpenCart

Cel mai adesea, luăm lucrurile de la sine înțeles. Dacă ceva funcționează așa cum era de așteptat, nu ne deranjăm despre funcționarea interioară a acesteia pentru a înțelege mecanismul care stă la baza acesteia. Sau să o punem într-un alt mod, nu vom săpați în ceva până când nu avem probleme!

În mod similar, mereu mă întrebau despre câteva concepte din OpenCart care au fost folosite în cadrul de bază, iar una dintre ele a fost clasa Proxy. Mi-a luat ceva timp să înțeleg și m-am gândit că este o chestie grozavă de a împărtăși cu tine, pentru că întotdeauna este distractiv să știi ceva nou.

Ce este o clasă proxy??

Deși veți găsi diverse materiale online care definesc termenul proxy, definiția din Wikipedia este frapantă și ușor de înțeles.

Un proxy, în cea mai generală formă, este o clasă care funcționează ca o interfață cu altceva.

Astfel, proxy-ul deleagă controlul obiectului pe care intenționează să-l folosească și acționează astfel în numele clasei actuale care a fost instanțiată. De fapt, modelul de design proxy este un model foarte popular care este folosit de cadrele populare după cum este necesar. Având în vedere faptul că o discuție despre metoda proxy în sine este un subiect atât de larg și în afara acestui articol, voi rezuma rapid ceea ce este folosit de cele mai multe ori:

  • Acționați ca un înveliș pentru a oferi funcționalități suplimentare.
  • Întârziați instanționarea obiectelor costisitoare, denumite și încărcări lenești.

În contextul OpenCart, am putea spune că modelul proxy este folosit pentru a adăuga funcționalitate la clasa proxy de bază. Acestea fiind spuse, clasa proxy de bază nu oferă nimic altceva decât câteva metode magice! Așa cum vom vedea în următoarea secțiune, clasa proxy este îmbogățită la timpul de execuție prin atașarea de proprietăți.

Înainte de a ne muta în secțiunea următoare, să examinăm rapid clasa proxy. Se află în Sistem / motor / proxy.php.

$ Cheie;  funcția publică __set ($ cheie, valoarea $) $ this -> $ key = valoarea $;  funcția publică __call ($ key, $ args) $ arg_data = array (); $ args = func_get_args (); foreach ($ args ca $ arg) dacă ($ arg instanceof Ref) $ arg_data [] = & $ arg-> getRef ();  altceva $ arg_data [] = & $ arg;  dacă (isset ($ this -> $ key)) returnă call_user_func_array ($ this -> $ key, $ arg_data);  altceva $ trace = debug_backtrace (); Ieșire('Înștiințare: Proprietate nedefinită: Proxy :: '. cheie cheie. "în ". $ trace [1] ['fișier']. ' pe net ". $ trace [1] ['linie']. '„); 

După cum puteți vedea, ea implementează trei metode magice: __obține(), __a stabilit(), și __apel(). Printre acestea, __apel() implementarea metodei este una importantă și vom reveni la ea destul de curând.

Cum funcționează clasa proxy cu modelul

În această secțiune, vă voi explica exact cum este un apel $ This-> model_catalog_category-> getCategory ($ category_id) funcționează din cutie.

De fapt, povestea începe cu următoarea declarație.

$ This-> a sarcinii> Model ( 'catalog / categorie');

În timpul procesului de bootstrare, cadrul OpenCart stochează toate obiectele generice la Registru astfel încât să poată fi extrași la voia lor. Ca urmare a acestui fapt, $ This-> încărcare apelul returnează Încărcător obiect al Registrului.

Încărcător clasa oferă diferite metode pentru a încărca componente diferite, dar ceea ce ne interesează aici este model metodă. Să tragem rapid fragmentul model metoda de la Sistem / motor / loader.php.

model de functie publica ($ route) // Sanitiza apelului $ route = preg_replace ('/ [^ a-zA-Z0-9 _ \ /] /', ', (string) $ route) $ this-> registry-> get ('event') -> trigger ('model /'. $ route. '/ before', array (& $ route) 'model_' str_replace (array ('/', '-', '.'), array ('_', '), $ route))) $ file = DIR_APPLICATION. . '.php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', '$ cale); dacă (is_file ($ file)) include_once ($ file); $ proxy = proxy nou (); foreach (metoda $ get_class_methods ($ class) ca metodă $) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'.  $ this-> registry-> set ('model_'. str_replace (array ('/', '-', '.' proxy);  altceva throw new \ Exception ('Eroare: nu a putut fi încărcat modelul'. $ route. '!');  // declanșează evenimentele $ this-> registry-> get ('event') -> trigger ('model /'. $ Route. '/ After', array (& $ route); 

Având în vedere exemplul menționat anterior, valoarea lui $ traseu argumentul este Catalog / categorie. În primul rând, valoarea $ traseu variabilă este dezinfectată, iar după aceea declanșează inainte de pentru a permite altor ascultători de module să modifice valoarea $ traseu variabil.

Apoi, verifică existența obiectului model solicitat în registru. Dacă Registrul deține obiectul solicitat, nu este necesară o altă prelucrare.

Dacă obiectul nu este găsit, urmează o procedură interesantă pentru ao încărca și este fragmentul pe care îl căutăm în contextul acestui articol.

Pentru început, pregătește calea fișierului modelului solicitat și îl încarcă dacă există.

... $ file = DIR_APPLICATION. 'model/' . $ rută. '.Php'; $ class = 'Model'. (fișier ($ file)) include_once ($ file); ... ... dacă este (este_file ($ file)); 

În consecință, instanțiază instanța împuternicire obiect.

$ proxy = proxy nou ();

Acum, atenție la următorul pentru buclă - face mult mai mult decât pare.

foreach (metoda $ get_class_methods ($ class) ca metodă $) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'. 

În cazul nostru, valoarea lui clasa $ ar trebui să fie ModelCatalogCategory. get_class_methods (clasa $) fragmentul încarcă toate metodele din ModelCatalogCategory clasa și buclele prin ea. Ce face în buclă? Să ne uităm atent.

În buclă, el cheamă suna inapoi din aceeași clasă. Este interesant de observat că metoda de returre returnează funcția apelată care este atribuită $ proxy obiect cu cheia ca nume de metoda. Desigur, obiectul proxy nu are astfel de proprietăți; acesta va fi creat în zbor folosind __a stabilit() magie!

Apoi, $ proxy obiect este adăugat la registru, astfel încât să poată fi extras mai târziu când este necesar. Priviți cu atenție componenta cheie a a stabilit metodă. În cazul nostru, ar trebui să fie model_catalog_category.

$ ',' - ','. '), array (' _ ',', '), (string) $ route), $ proxy );

La sfârșit, o să sune după pentru a permite altor ascultători de module să modifice valoarea $ traseu variabil.

Aceasta este o parte din poveste.

Hai să vedem ce se întâmplă exact când folosiți următoarele în controlerul dvs..

$ This-> model_catalog_category-> getCategory ($ category_id);

$ This-> model_catalog_category fragmentul încearcă să găsească o potrivire pentru model_catalog_category cheie în Registru. Dacă vă întrebați cum, doar căutați în Controlor clasa în Sistem / motor / controller.php fișier - acesta oferă __obține() metode magice care o fac.

După cum tocmai am discutat, ar trebui să ne întoarcem $ proxy obiect atribuit acelei anumite chei. Apoi, încearcă să apeleze getCategory pe acest obiect. Dar clasa Proxy nu implementează o astfel de metodă, deci cum funcționează aceasta?

__apel() metoda magică vine la salvare! Ori de câte ori apelați o metodă care nu există în clasă, comanda este transferată la __apel() magie.

Să explorăm în detaliu pentru a înțelege ce se întâmplă. Deschideți fișierul de clasă proxy și acordați atenție acestei metode.

cheie $ conține numele funcției care se numește-getCategory. Pe de altă parte, $ args conține argumente transmise metodei și ar trebui să fie o matrice a unui element care deține id-ul categoriei care este trecut.

Apoi, există o matrice $ arg_data care stochează referințele argumentelor. Sincer, nu sunt sigur dacă codul Exemplu $ arg Ref are sens sau nu acolo. Dacă cineva știe de ce este acolo, aș fi fericit să învăț.

În plus, încearcă să verifice existența cheie $ proprietate în $ proxy obiect, și rezultă în ceva de genul acesta.

dacă (isset ($ this-> getCategory)) 

Reamintim că mai devreme am atribuit toate metodele de ModelCatalogCategory clasa ca proprietăți ale $ proxy obiect utilizând a pentru buclă. Pentru comoditatea dvs., voi reintroduce codul din nou.

... foreach (metoda get_class_methods ($ class) ca metodă $) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'.  ... 

Deci, ar trebui să fie acolo, și ar trebui să ne întoarcă, de asemenea, funcția apelabilă! În cele din urmă, aceasta numește funcția care poate fi apelată cu ajutorul funcției call_user_func_array funcția prin trecerea funcției callabile în sine și a argumentelor metodei.

Acum, să ne redirecționăm atenția asupra definiției callabile a funcției în sine. Îmi iau fragmentul din suna inapoi metoda definită în Sistem / motor / loader.php.

... funcția ($ args) utilizează ($ registry, & $ route) static $ model = array (); $ output = null; // Activați pre evenimentele $ result = $ registry-> get ('event') -> trigger ('model /'. $ Route '/ before', array (& $ route, & $ args, & output) ); dacă ($ rezultat) return $ result;  // Stocați obiectul modelului dacă (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'model/' . substr ($ rută, 0, strrpos ($ rută, '/')). '.Php'; $ class = 'Model'. (fișier ($ file)) include_once ($) () () () fișierul) $ model [$ route] = noua clasă $ ($ registry); altceva aruncă o nouă \ Exception (' ) $ method = substr ($ route, '/') + 1); $ callable = array ($ model [$ route]; ($ callable)) $ output = call_user_func_array ($ callable, $ args); altceva arunca noua \ Exceptie ('Eroare: evenimente $ $ $ = $ registrar-> get ('event') -> trigger ('model /'. $ route. '/ after', array (& $ route, & $ args, rezultatul) return $ result; return $ output;; ... 

Deoarece este o funcție anonimă, ea a păstrat valorile sub formă de $ registru și $ traseu variabilelor care au fost transferate mai devreme în metoda de apel invers. În acest caz, valoarea lui $ traseu variabilă ar trebui să fie Catalog / categorie / getCategory.

În afară de aceasta, dacă privim la fragmentul important în acea funcție, instanțiază ModelCatalogCategory obiect și îl stochează într-o statică model de $ mulțime.

... // Stocați modelul obiect dacă (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'model/' . substr ($ rută, 0, strrpos ($ rută, '/')). '.Php'; $ class = 'Model'. (fișier ($ file)) include_once ($) () () () fișierul) $ model [$ route] = noua clasă $ ($ registry); altceva aruncă o nouă \ Exception (' )). ')'; ... 

Iată un fragment care apreciază numele metodei care trebuie apelat utilizând $ traseu variabil.

$ method = substr ($ rută, strrpos ($ rută, '/') + 1);

Deci, avem o referință de obiect și un nume de metodă care ne permite să o numim folosind call_user_func_array funcţie. Fragmentul următor face exact acest lucru!

... dacă (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args);  altceva aruncați o nouă \ Excepție ("Eroare: Could not call model / '. $ route.' '');  ... 

La sfârșitul metodei, rezultatul rezultat este returnat prin $ ieșire variabil. Și da, asta eo altă parte a povestirii!

Am ignorat în mod intenționat pre și post cod de evenimente care vă permite să înlocuiți metodele din clasele de bază OpenCart. Folosind acest lucru, puteți suprascrie orice metodă de clasă de bază și puteți modifica-o după necesitățile dvs. Dar să păstrăm asta pentru o altă zi, pentru că va fi prea mult pentru a se încadra într-un singur articol!

Deci așa funcționează cu totul. Sper că ar trebui să fiți mai încrezători în apelurile de stenogramă OpenCart și în ceea ce privește lucrările lor interne.

Concluzie

Ceea ce am discutat astăzi este unul dintre conceptele interesante și ambigue în OpenCart: utilizarea metodei Proxy într-un cadru care să sprijine convențiile de stenogramă pentru a apela metode de modelare. Sper ca articolul să fie destul de interesant și să vă îmbogățească cunoștințele despre cadrul OpenCart.

Mi-ar plăcea să știu feedback-ul dvs. cu privire la acest lucru, și dacă vă simțiți că ar trebui să acopere astfel de subiecte în articolele mele viitoare, nu ezitați să renunțe la o linie despre asta!

Cod