Het decoderen van de proxy-klasse in OpenCart

Vaker wel dan niet nemen we dingen als vanzelfsprekend aan. Als iets werkt zoals verwacht, doen we er niet om de interne werking ervan om het onderliggende mechanisme te begrijpen. Of om het anders te zeggen, we graven niet iets in totdat we in een soort van probleem zitten!

Op dezelfde manier vroeg ik me altijd af over een aantal concepten in OpenCart die werden gebruikt in het onderliggende framework, en een daarvan was de Proxy-klasse. Het kostte me een tijdje om het te begrijpen en ik vond het geweldige dingen om met je te delen omdat het altijd leuk is om iets nieuws te weten.

Wat is een proxy-klasse?

Hoewel u online verschillende materialen zult vinden die de term proxy definiëren, is de definitie van Wikipedia opvallend en gemakkelijk te begrijpen.

Een proxy, in zijn meest algemene vorm, is een klasse die functioneert als een interface naar iets anders.

Dus de proxy delegeert het besturingselement naar het object dat het wil gebruiken en handelt dus namens de eigenlijke klasse die is geïnstantieerd. In feite is het proxy-ontwerppatroon een zeer populair patroon dat wordt gebruikt door populaire raamwerken waar nodig. Gezien het feit dat een bespreking van de proxy-methode op zich zo'n breed onderwerp is en buiten de reikwijdte van dit artikel valt, zal ik snel samenvatten waar het voor het grootste deel van de tijd voor gebruikt wordt:

  • Handel als een verpakking om extra functionaliteit te bieden.
  • Stel het maken van dure objecten uit, ook wel lui laden genoemd.

In de context van OpenCart kunnen we zeggen dat het proxypatroon wordt gebruikt om functionaliteit toe te voegen aan de basisproxyklasse. Dat gezegd hebbende, biedt de basis proxy-klasse zelf niets anders dan een paar magische methoden! Zoals we in de volgende sectie zullen zien, wordt de proxyklasse tijdens runtime verrijkt door er eigenschappen aan toe te voegen.

Voordat we naar het volgende gedeelte gaan, laten we de proxyklasse snel bekijken. Het bevindt zich in system / motor / proxy.php.

$ Key;  public function __set ($ key, $ value) $ this -> $ key = $ waarde;  openbare functie __call ($ key, $ args) $ arg_data = array (); $ args = func_get_args (); foreach ($ args als $ arg) if ($ arg instanceof Ref) $ arg_data [] = & $ arg-> getRef ();  else $ arg_data [] = & $ arg;  if (isset ($ this -> $ key)) return call_user_func_array ($ this -> $ key, $ arg_data);  else $ trace = debug_backtrace (); Uitgang('Merk op: Undefined property: Proxy :: '. $ sleutel. 'in '. $ trace [1] ['file']. ' online '. $ trace [1] ['line']. ''); 

Zoals je ziet, implementeert het drie magische methoden: __krijgen(), __set (), en __call (). Onder hen, de __call () methode-implementatie is een belangrijke, en we zullen er snel op terug komen.

Hoe de proxyklasse werkt met het model

In deze sectie zal ik uitleggen hoe een oproep precies zo is $ This-> model_catalog_category-> getCategory ($ category_id) werkt uit de doos.

In feite begint het verhaal met de volgende verklaring.

$ This-> load-> model ( 'catalogus / categorie');

Tijdens bootstrappen slaat het OpenCart-framework alle generieke objecten op in de Registry object zodat ze naar believen kunnen worden opgehaald. Als gevolg hiervan, de $ This-> load oproep retourneert de lader object van het register.

De lader class biedt verschillende methoden om verschillende componenten te laden, maar waar we hier in geïnteresseerd zijn, is de model- methode. Laten we snel het fragment van de model- methode van system / motor / loader.php.

public function model ($ route) // Sanitize the call $ route = preg_replace ('/ [^ a-zA-Z0-9 _ \ /] /', ', (string) $ route); // Activeer de pre-evenementen $ this-> registry-> get ('event') -> trigger ('model /'. $ route. '/ before', array (& $ route)); if (! $ this-> register-> has ( 'model_'. str_replace (array ('/', '-', '.'), array ('_', ","), $ route))) $ file = DIR_APPLICATION. 'model /'. $ route . '.php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", $ route); if (is_file ($ bestand)) include_once ($ bestand); $ proxy = nieuwe proxy (); foreach (get_class_methods ($ class) als $ methode) $ proxy -> $ method = $ this-> callback ($ this-> register, $ route. '/'. $ methode);  $ this-> registry-> set ('model_'. str_replace (array ('/', '-', '.'), array ('_', ','), (string) $ route), $ proxy);  else throw new \ Exception ('Fout: kon model niet laden'. $ route. '!');  // Start de postgebeurtenissen $ this-> registry-> get ('event') -> trigger ('model /'. $ Route. '/ After', array (& $ route)); 

Gezien het bovengenoemde voorbeeld, is de waarde van de $ route argument is catalogus / category. Ten eerste, de waarde van de $ route variabele is gedesinfecteerd, en daarna triggert het de voor gebeurtenis om andere module luisteraars toe te staan ​​de waarde van de te wijzigen $ route veranderlijk.

Vervolgens wordt het bestaan ​​van het gevraagde modelobject in het register gecontroleerd. Als het register het gevraagde object bevat, is verdere verwerking niet nodig.

Als het object niet wordt gevonden, volgt het een interessante procedure om het te laden, en het is het fragment dat we zoeken in de context van dit artikel.

Om te beginnen, bereidt het het bestandspad van het gevraagde model voor en laadt het als het bestaat.

... $ file = DIR_APPLICATION. 'model /'. $ route. '.Php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", $ route); if (is_file ($ bestand)) include_once ($ file); ... ... 

Hierna richt het de volmacht voorwerp.

$ proxy = nieuwe proxy ();

Let nu op de volgende voor loop-it doet veel meer dan het lijkt.

foreach (get_class_methods ($ class) als $ methode) $ proxy -> $ method = $ this-> callback ($ this-> register, $ route. '/'. $ methode); 

In ons geval is de waarde van $ klasse zou moeten zijn ModelCatalogCategory. De get_class_methods ($ klasse) snippet laadt alle methoden van de ModelCatalogCategory klasse en loopt er doorheen. Wat doet het in de loop? Laten we goed kijken.

In de lus roept het de Bel terug methode van dezelfde klasse. Het is interessant om op te merken dat de callback-methode de functie callable retourneert die is toegewezen aan de $ proxy object met de sleutel als de methode naam. Natuurlijk heeft het proxy-object geen dergelijke eigenschappen; het wordt on the fly gemaakt met behulp van de __set () magische methode!

Vervolgens de $ proxy object wordt toegevoegd aan het register zodat het later kan worden opgehaald wanneer dat nodig is. Bekijk het belangrijkste onderdeel van de reeks methode. In ons geval zou het moeten zijn model_catalog_category.

$ this-> registry-> set ('model_'. str_replace (array ('/', '-', '.'), array ('_', ","), (string) $ route), $ proxy );

Aan het eind zal het de na gebeurtenis om andere module luisteraars toe te staan ​​de waarde van de te wijzigen $ route veranderlijk.

Dat is een deel van het verhaal.

Laten we doornemen wat er precies gebeurt wanneer u het volgende in uw controller gebruikt.

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

De $ This-> model_catalog_category snippet probeert een overeenkomst te vinden voor de model_catalog_category sleutel in het register. Als je je afvraagt ​​hoe, kijk dan gewoon in de controleur klassendefinitie in de system / motor / controller.php file-it levert de __krijgen() magische methode die het doet.

Zoals we zojuist besproken hebben, zou dat het $ proxy object dat aan die bepaalde sleutel is toegewezen. Vervolgens probeert het de getCategory methode op dat object. Maar de Proxy-klasse implementeert zo'n methode niet, dus hoe gaat dat werken?

De __call () magische methode komt te hulp! Telkens wanneer u een methode aanroept die niet in de klasse bestaat, wordt de besturing overgedragen naar de __call () magische methode.

Laten we het in detail verkennen om te begrijpen wat er aan de hand is. Open het bestand Proxy-klasse en let op die methode.

De $ key bevat de naam van de functie die wordt aangeroepen-getCategory. Anderzijds, $ args bevat argumenten die aan de methode zijn doorgegeven en het moet een array zijn van één element dat de categorie-ID bevat die wordt doorgegeven.

Vervolgens is er een array $ arg_data dat referenties van argumenten opslaat. Eerlijk gezegd weet ik niet zeker of de code $ arg instanceof Ref is logisch of niet waar. Als iemand weet waarom het er is, wil ik het graag leren.

Verder probeert het het bestaan ​​van de $ key eigendom in de $ proxy object, en het resulteert in zoiets als dit.

if (isset ($ this-> getCategory)) 

Bedenk dat we eerder alle methoden van ModelCatalogCategory klasse als eigenschappen van de $ proxy object met behulp van een voor lus. Voor uw gemak, zal ik die code opnieuw plakken.

... foreach (get_class_methods ($ class) als $ methode) $ proxy -> $ method = $ this-> callback ($ this-> register, $ route. '/'. $ Methode);  ... 

Dus het zou daar moeten zijn, en het zou ons ook de functie moeten teruggeven die kan worden ingeroepen! En ten slotte noemt het die functie die op te roepen is met de call_user_func_array functie door de functie callable zelf en de methode-argumenten door te geven.

Laten we nu onze aandacht richten op de functie opvraagbare definitie zelf. Ik haal het fragment van de Bel terug methode gedefinieerd in system / motor / loader.php.

... functie ($ args) gebruik ($ register, & $ route) static $ model = array (); $ output = null; // Start de voorgeprogrammeerde gebeurtenissen $ result = $ registry-> get ('event') -> trigger ('model /'. $ Route. '/ Before', array (& $ route, & $ args, & $ output) ); if ($ resultaat) return $ resultaat;  // Sla het modelobject op als (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'model /'. substr ($ route, 0, strrpos ($ route, '/')). '.Php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ route, 0, strrpos ($ route, '/'))); if (is_file ($ file)) include_once ($ bestand); $ model [$ route] = nieuwe $ class ($ register); else throw new \ Exception ('Fout: Kon model niet laden'. substr ($ route, 0, strrpos ($ route, '/' )). '!'); $ method = substr ($ route, strrpos ($ route, '/') + 1); $ callable = array ($ model [$ route], $ methode); if (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args); else else throw new \ Exception ('Error: Could not call model /'. $ route. '!'); // Activeer het bericht events $ result = $ registry-> get ('event') -> trigger ('model /'. $ route. '/ after', array (& $ route, & $ args, & $ output)); if ($ resultaat) return $ resultaat; return $ output;; ... 

Omdat het een anonieme functie is, heeft het de waarden in de vorm bewaard $ register en $ route variabelen die eerder zijn doorgegeven aan de callback-methode. In dit geval is de waarde van de $ route variabele zou moeten zijn catalogus / category / getCategory.

Afgezien daarvan, als we kijken naar het belangrijke fragment in die functie, maakt het de ModelCatalogCategory object en slaat het op in een statische $ model rangschikking.

... // Sla het modelobject op als (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'model /'. substr ($ route, 0, strrpos ($ route, '/')). '.Php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ route, 0, strrpos ($ route, '/'))); if (is_file ($ file)) include_once ($ bestand); $ model [$ route] = nieuwe $ class ($ register); else throw new \ Exception ('Fout: Kon model niet laden'. substr ($ route, 0, strrpos ($ route, '/' )). '!'); ... 

En hier is een fragment dat de methode-naam grijpt die moet worden aangeroepen met behulp van de $ route veranderlijk.

$ methode = substr ($ route, strrpos ($ route, '/') + 1);

Dus we hebben een objectreferentie en een methode naam waarmee we het kunnen gebruiken met de call_user_func_array functie. Het volgende fragment doet precies dat!

... if (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args);  else throw new \ Exception ('Error: Could not call model /'. $ route. '!');  ... 

Aan het einde van de methode wordt het resulterende resultaat geretourneerd via de $ uitgang variabel. En ja, dat is een ander deel van het verhaal!

Ik heb opzettelijk de pre en post gebeurtenissencode waarmee u methoden van de kern OpenCart-klassen kunt overschrijven. Gebruikend dat, zou u om het even welke kernklassemethode kunnen opheffen en het aanpassen volgens uw behoefte. Maar laten we dat een andere dag houden, omdat het te veel is om in één artikel te passen!

Dus dat is hoe het helemaal werkt. Ik hoop dat je meer zelfvertrouwen zult hebben over die steno's van OpenCart en over hun innerlijke werking.

Conclusie

Wat we zojuist hebben besproken, is een van de interessante en dubbelzinnige concepten in OpenCart: het gebruik van de proxy-methode in een raamwerk ter ondersteuning van steno-conventies voor het aanroepen van modelmethoden. Ik hoop dat het artikel interessant genoeg was en je kennis van het OpenCart-framework verrijkte.

Ik zou graag uw feedback hierover willen weten en als u vindt dat ik dergelijke onderwerpen in mijn aankomende artikelen zou moeten bespreken, aarzel dan niet om er een regel over te laten!