Dekoding av proxy-klassen i OpenCart

Oftere enn ikke, tar vi ting for gitt. Hvis noe fungerer som forventet, bryter vi oss ikke om den indre virkningen av den for å forstå den underliggende mekanismen. Eller for å si det på en annen måte, graver vi ikke inn noe før vi er i en slags problemer!

Tilsvarende lurte jeg alltid på et par konsepter i OpenCart som ble brukt i det underliggende rammeverket, og en av dem var Proxy-klassen. Det tok meg en stund å forstå det, og jeg trodde det er gode ting å dele med deg, da det alltid er morsomt å vite noe nytt.

Hva er en proxy klasse?

Selv om du finner ulike materialer på nettet som definerer begrepet proxy, er definisjonen fra Wikipedia slående og lett å forstå.

En proxy, i sin mest generelle form, er en klasse som fungerer som et grensesnitt til noe annet.

Så proxy delegerer kontrollen til objektet den har til hensikt å bruke, og dermed fungerer på vegne av den faktiske klassen som er instansiert. Faktisk er proxy design mønsteret et veldig populært mønster som brukes av populære rammer som nødvendig. Tatt i betraktning det faktum at en diskusjon av proxy-metoden i seg selv er så bred et tema og utenom denne artikkelen, vil jeg raskt oppsummere hva den brukes mesteparten av tiden:

  • Gjør som en wrapper for å gi ekstra funksjonalitet.
  • Forsink instantiering av dyre gjenstander, også referert til som lat lasting.

I sammenheng med OpenCart kan vi si at proxy-mønsteret brukes til å legge til funksjonalitet til basen proxy-klassen. Når det er sagt, gir basen proxy klassen seg ikke noe annet enn et par magiske metoder! Som vi ser i neste avsnitt, blir proxy-klassen beriket i kjøretid ved å legge ved egenskaper til den.

Før vi går inn i neste del, la oss se på proxy-klassen raskt. Den ligger i Systemet / motor / proxy.php.

$ Nøkkel;  offentlig funksjon __set ($ nøkkel, $ verdi) $ dette -> $ key = $ verdi;  offentlig funksjon __call ($ key, $ args) $ arg_data = array (); $ args = func_get_args (); foreach ($ args som $ arg) hvis ($ arg instanceof Ref) $ arg_data [] = & $ arg-> getRef ();  ellers $ arg_data [] = & $ arg;  hvis (isset ($ this -> $ key)) return call_user_func_array ($ this -> $ key, $ arg_data);  ellers $ trace = debug_backtrace (); exit('Legge merke til: Udefinert eiendom: Proxy :: '. $ nøkkel. 'i '. $ spor [1] ['fil']. ' på nett '. $ spor [1] ['linje']. ''); 

Som du kan se, implementerer den tre magiske metoder: __få(), __sett(), og __anrop(). Blant dem, den __anrop() Metoden implementering er en viktig, og vi kommer snart tilbake til det.

Hvordan Proxy-klassen fungerer med modellen

I denne delen forklarer jeg hvor nøyaktig en samtale er $ Dette-> model_catalog_category-> getCategory ($ category_id) virker ut av esken.

Faktisk begynner historien med følgende erklæring.

$ Dette-> last> modell ( 'Katalog / kategori');

Under bootstrapping lagrer OpenCart-rammeverket alle generiske objekter til Registry objekt slik at de kan hentes etter vilje. Som et resultat av det, $ Dette-> load anrop returnerer loader objekt fra registret.

De loader klassen gir ulike metoder for å laste forskjellige komponenter, men det vi er interessert i her er modell metode. La oss raskt trekke kapittelet til modell metode fra Systemet / motor / loader.php.

offentlig funksjonsmodell ($ rute) // Gjenopprett anropet $ route = preg_replace ('/ [^ a-zA-Z0-9 _ \ /] /', ', (strengen) $ rute); // Utløs pre-hendelsene $ this-> registry-> get ('event') -> utløser ('model /'. $ rute. '/ før', array (& $ rute)), hvis (! $ this-> registry-> har 'model_'. str_replace (array ('/', '-', '.'), array ('_', ','), $ rute))) $ file = DIR_APPLICATION. . '.php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", $ rute); hvis (is_file ($ file)) include_once ($ file); $ proxy = ny proxy (); $ proxy -> $ method = $ this-> tilbakeringing ($ denne-> registret, $ rute. '/'. $ metode);  $ this-> registry-> set ('model_' .str_replace (array ('/', '-', '.'), array ('_', ' proxy);  ellers kast nytt \ Unntak ('Feil: Kunne ikke laste modell'. $ rute. '!');  // Trigger posthendelsene $ this-> registry-> get ('event') -> trigger ('model /'. $ Rute. '/ Etter', array (& $ rute)); 

Tatt i betraktning det ovennevnte eksempel, verdien av $ rute argumentet er Katalog / kategori. Først verdien av $ rute variabel er sanitized, og etter at den utløser før hendelse for å tillate andre modullyttere å endre verdien av $ rute variabel.

Deretter kontrollerer den eksistensen av det forespurte modellobjektet i registret. Hvis registeret inneholder det forespurte objektet, er det ikke nødvendig med ytterligere behandling.

Hvis objektet ikke er funnet, følger det en interessant prosedyre for å laste den, og det er koden vi leter etter i sammenheng med denne artikkelen.

Til å begynne med forbereder den filbanen til den forespurte modellen og laster den hvis den eksisterer.

... $ file = DIR_APPLICATION. 'modell /'. $ rute. 'Php'; $ class = 'Modell'. preg_replace ('/ [^ a-zA-Z0-9] /', ", $ rute); hvis (is_file ($ file)) include_once ($ file); ... ... 

Etter det, instantiates den proxy gjenstand.

$ proxy = ny proxy ();

Nå, vær oppmerksom på det neste til loop-det gjør mye mer enn det virker.

$ proxy -> $ method = $ this-> tilbakeringing ($ denne-> registret, $ rute. '/'. $ metode); 

I vårt tilfelle, verdien av $ class bør være ModelCatalogCategory. De get_class_methods ($ klasse) bunten laster alle metoder for ModelCatalogCategory klasse og løkker gjennom den. Hva gjør det i løkken? La oss se nøye ut.

I løkken kalles det Ring tilbake metode av samme klasse. Det er interessant å merke seg at tilbakeringingsmetoden returnerer funksjonen som kan kalles som er tildelt til $ proxy objekt med nøkkelen som metodenavnet. Selvfølgelig har proxy objektet ingen slike egenskaper; det vil bli opprettet på fluen ved hjelp av __sett() magisk metode!

Deretter $ proxy objekt er lagt til registeret slik at det kan hentes senere når det trengs. Ta en nærmere titt på nøkkelkomponenten i sett metode. I vårt tilfelle bør det være model_catalog_category.

$ this-> registry-> set ('model_' .str_replace (array ('/', '-', '.'), array ('_', ','), (streng) $ rute), $ proxy );

På slutten vil det ringe til etter hendelse for å tillate andre modullyttere å endre verdien av $ rute variabel.

Det er en del av historien.

La oss gå gjennom hva som skjer nøyaktig når du bruker følgende i kontrolleren din.

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

De $ Dette-> model_catalog_category snippet prøver å finne en kamp for model_catalog_category tast inn registeret. Hvis du lurer på hvordan du bare ser på Controller klasse definisjon i Systemet / motor / controller.php fil-det gir __få() magisk metode som gjør det.

Som vi nettopp har diskutert, bør det returnere $ proxy objekt som er tilordnet den aktuelle nøkkelen. Deretter prøver den å ringe getCategory metode på objektet. Men proxy-klassen implementerer ikke en slik metode, så hvordan går det til å fungere?

De __anrop() magisk metode kommer til redning! Når du ringer en metode som ikke finnes i klassen, overføres kontrollen til __anrop() magisk metode.

La oss utforske det i detalj for å forstå hva som skjer. Åpne proxy-klassefilen og vær oppmerksom på den metoden.

De $ nøkkel inneholder navnet på funksjonen som blir kalt-getCategory. På den andre siden, $ args inneholder argumenter overført til metoden, og det bør være en rekke av ett element som holder kategorien id som blir sendt.

Deretter er det en matrise $ arg_data som lagrer referanser av argumenter. Oppriktig er jeg ikke sikker på om koden $ arg instanceof Ref gjør noe fornuftig eller ikke der. Hvis noen vet hvorfor det er der, ville jeg være glad for å lære.

Videre forsøker den å kontrollere eksistensen av $ nøkkel eiendom i $ proxy objekt, og det resulterer i noe sånt.

hvis (isset ($ this-> getCategory)) 

Husk at tidligere vi tildelte alle metodene til ModelCatalogCategory klasse som egenskaper av $ proxy objekt ved hjelp av a til sløyfe. For enkelhets skyld legger jeg inn koden på nytt.

... foreach ($ klasse) som $ metode) $ $ proxy -> $ method = $ this-> tilbakeringing ($ denne-> registret, $ rute. '/'. $ Metode);  ... 

Så det burde være der, og det bør også returnere funksjonen som kan kalles! Og til slutt kaller det den funksjonen som kan kalles ved hjelp av call_user_func_array funksjon ved å overføre funksjonen som kan kalles og metoden argumenter.

La oss nå avlede oppmerksomheten til selve funksjonen som kan kalles. Jeg tar tak i koden fra Ring tilbake metode definert i Systemet / motor / loader.php.

... funksjon ($ args) bruk ($ register, & $ rute) static $ model = array (); $ utgang = null; // Trigger pre events $ result = $ registry-> get ('event') -> trigger ('model /'. $ Rute. '/ Før', array (& $ rute, & $ args, & $ output) ); hvis ($ resultat) return $ result;  // Lagre modellobjektet hvis (! Isset ($ model [$ rute])) $ file = DIR_APPLICATION. 'modell /'. substr ($ rute, 0, strrpos ($ rute, '/')). 'Php'; $ class = 'Modell'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ rute, 0, strrpos ($ rute, '/')); hvis (is_file ($ file)) include_once $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ) $. $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ($ callable)) $ output = call_user_func_array ($ callable, $ args); annet kaster nytt \ Unntak ('Feil: Kunne ikke ringe modell /'. $ rute. '!'); // Trigger innlegget hendelser $ result = $ registry-> get ('event') -> trigger ('model /'. $ rute. '/ etter', array (& $ rute, & $ args, & $ output)) resultat) return $ result; returnere $ output;; ... 

Som det er en anonym funksjon, har den bevart verdiene i form av $ registret og $ rute variabler som ble sendt tidligere til tilbakekallingsmetoden. I dette tilfellet er verdien av $ rute variabel skal være Katalog / kategori / getCategory.

Bortsett fra det, hvis vi ser på det viktige utdraget i den funksjonen, instanserer den ModelCatalogCategory objekt og lagrer den i en statisk $ modell matrise.

... // Lagre modellobjektet hvis (! Isset ($ model [$ rute])) $ file = DIR_APPLICATION. 'modell /'. substr ($ rute, 0, strrpos ($ rute, '/')). 'Php'; $ class = 'Modell'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ rute, 0, strrpos ($ rute, '/')); hvis (is_file ($ file)) include_once $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ )). '!'); ... 

Og her er en utdrag som tar tak i metodenavnet som må kalles ved hjelp av $ rute variabel.

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

Så vi har en objektreferanse og et metodenavn som tillater oss å ringe det ved hjelp av call_user_func_array funksjon. Følgende utdrag gjør nettopp det!

... hvis (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args);  ellers kast nytt \ Unntak ('Feil: Kunne ikke ringe modell /'. $ rute. '!');  ... 

Ved slutten av metoden blir det resulterende resultatet returnert via $ utgang variabel. Og ja, det er en annen del av historien!

Jeg har bevisst ignorert pre og post hendelseskode som lar deg overstyre metoder for kjernen OpenCart-klasser. Ved å bruke det, kan du overstyre noen kjerneklassemetode og justere den etter behov. Men la oss beholde det for en annen dag, da det blir for mye å passe inn i en enkelt artikkel!

Så det virker helt hvordan det fungerer. Jeg håper at du bør være mer trygg på de kortene som OpenCart ringer og om deres indre arbeid.

Konklusjon

Det vi nettopp har diskutert i dag, er et av de interessante og tvetydige konseptene i OpenCart: bruken av proxy-metoden i et rammeverk for å støtte stenografi-konvensjoner for å kalle modellmetoder. Jeg håper artikkelen var interessant nok og beriket din OpenCart-rammeverkskunnskap.

Jeg vil gjerne vite tilbakemeldingen din om dette, og hvis du føler at jeg skal dekke slike emner i mine kommende artikler, ikke nøl med å slippe en linje om det!