Decodifica della classe proxy in OpenCart

Più spesso, prendiamo le cose per scontate. Se qualcosa funziona come previsto, non ci preoccupiamo del funzionamento interno di esso per capire il meccanismo sottostante. O per dirla in altro modo, non scaviamo in qualcosa finché non siamo in qualche guaio!

Allo stesso modo, mi chiedevo sempre un paio di concetti in OpenCart che erano usati nel framework sottostante, e uno di questi era la classe Proxy. Mi ci è voluto un po 'per capirlo, e ho pensato che fosse una cosa fantastica da condividere con te dato che è sempre divertente conoscere qualcosa di nuovo.

Che cos'è una classe proxy?

Anche se troverai vari materiali online che definiscono il termine proxy, la definizione di Wikipedia è sorprendente e facile da capire.

Un proxy, nella sua forma più generale, è una classe che funziona da interfaccia con qualcos'altro.

Quindi il proxy delega il controllo all'oggetto che intende utilizzare, e quindi agisce per conto della classe effettiva che viene istanziata. In effetti, il pattern di progettazione proxy è un pattern molto popolare che viene utilizzato dai framework più popolari, se necessario. Considerando il fatto che una discussione sul metodo proxy di per sé è un argomento così ampio e fuori dagli scopi di questo articolo, riassumerò rapidamente quello che è stato utilizzato per la maggior parte del tempo:

  • Agisci come un wrapper per fornire funzionalità aggiuntive.
  • Ritarda la creazione di istanze di oggetti costosi, chiamati anche caricamento lento.

Nel contesto di OpenCart, potremmo dire che il pattern proxy è usato per aggiungere funzionalità alla classe proxy di base. Detto questo, la classe proxy di base non fornisce nulla tranne un paio di metodi magici! Come vedremo nella prossima sezione, la classe proxy è arricchita in fase di runtime collegando le proprietà ad essa.

Prima di passare alla sezione successiva, diamo una rapida occhiata alla classe proxy. Risiede dentro Sistema / motore / proxy.php.

$ Key;  public function __set ($ key, $ value) $ this -> $ key = $ valore;  public function __call ($ key, $ args) $ arg_data = array (); $ args = func_get_args (); foreach ($ args as $ 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 (); Uscita('Avviso: Proprietà non definita: Proxy :: '. $ chiave. ' nel '. $ trace [1] ['file']. ' in linea '. $ trace [1] ['line']. ''); 

Come puoi vedere, implementa tre metodi magici: __ottenere(), __impostato(), e __chiamata(). Tra questi, il __chiamata() l'implementazione del metodo è importante e ci torneremo presto.

Come funziona la classe proxy con il modello

In questa sezione, spiegherò come esattamente una chiamata come $ This-> model_catalog_category-> getCategory ($ category_id) funziona fuori dalla scatola.

In realtà, la storia inizia con la seguente dichiarazione.

$ This-> load-> modello ( 'catalogo / categoria');

Durante il bootstrap, il framework OpenCart memorizza tutti gli oggetti generici su Registro oggetto in modo che possano essere recuperati a volontà. Di conseguenza, il $ This-> load la chiamata restituisce il caricatore oggetto dal registro.

Il caricatore class fornisce vari metodi per caricare diversi componenti, ma ciò che ci interessa qui è il modello metodo. Prendiamo rapidamente il frammento di modello metodo da Sistema / motore / loader.php.

modello di funzione pubblico ($ route) // Sanitizza la chiamata $ route = preg_replace ('/ [^ a-zA-Z0-9 _ \ /] /', ", (stringa) $ percorso); // Attiva i pre-eventi $ this-> registry-> get ('event') -> trigger ('model /'. $ route. '/ before', array (& $ route)); if (! $ this-> registry-> ha ( 'model_'. str_replace (array ('/', '-', '.'), array ('_', ","), $ route))) $ file = DIR_APPLICATION. 'model /'. $ percorso . '.php'; $ class = 'Model'. preg_replace ('/ [^ a-zA-Z0-9] /', ", $ route); if (is_file ($ file)) include_once ($ file); $ proxy = new Proxy (); foreach (get_class_methods ($ class) come $ method) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'. $ method);  $ this-> registry-> set ('model_'. str_replace (array ('/', '-', '.'), array ('_', ","), (stringa) $ route), $ proxy);  else throw new \ Exception ('Errore: impossibile caricare il modello'. $ route. '!');  // Attiva gli eventi di post $ this-> registry-> get ('event') -> trigger ('model /'. $ Route. '/ After', array (& $ route)); 

Considerando l'esempio di cui sopra, il valore di $ route argomento è Catalogo / categoria. Innanzitutto, il valore del $ route la variabile viene disinfettata e, successivamente, attiva il prima evento per consentire ad altri ascoltatori di moduli di modificare il valore di $ route variabile.

Successivamente, controlla l'esistenza dell'oggetto modello richiesto nel registro. Se il Registro detiene l'oggetto richiesto, non è necessaria alcuna ulteriore elaborazione.

Se l'oggetto non viene trovato, segue una procedura interessante per caricarlo, ed è lo snippet che stiamo cercando nel contesto di questo articolo.

Per cominciare, prepara il percorso del file del modello richiesto e lo carica se esiste.

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

In seguito, istanzia il delega oggetto.

$ proxy = new Proxy ();

Ora, presta attenzione a quello successivo per loop: fa molto più di quello che sembra.

foreach (get_class_methods ($ class) come $ method) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'. $ method); 

Nel nostro caso, il valore di $ classe dovrebbe essere ModelCatalogCategory. Il get_class_methods ($ class) lo snippet carica tutti i metodi di ModelCatalogCategory classe e lo attraversa. Cosa fa nel ciclo? Diamo un'occhiata da vicino.

Nel ciclo, chiama il richiama metodo della stessa classe. È interessante notare che il metodo di callback restituisce la funzione callable assegnata a $ delega oggetto con la chiave come nome del metodo. Ovviamente, l'oggetto proxy non ha alcuna proprietà del genere; sarà creato al volo usando il __impostato() metodo magico!

Quindi, il $ delega l'oggetto viene aggiunto al Registro di sistema in modo che possa essere recuperato in seguito, quando necessario. Dai un'occhiata da vicino al componente chiave del impostato metodo. Nel nostro caso, dovrebbe essere model_catalog_category.

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

Alla fine, chiamerà il dopo evento per consentire ad altri ascoltatori di moduli di modificare il valore di $ route variabile.

Questa è una parte della storia.

Analizziamo cosa succede esattamente quando usi il seguente controller.

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

Il $ This-> model_catalog_category snippet tenta di trovare una corrispondenza per il model_catalog_category digitare il registro. Se ti stai chiedendo come, guarda nel controllore definizione di classe nel Sistema / motore / controller.php file: fornisce il __ottenere() metodo magico che lo fa.

Come abbiamo appena discusso, dovrebbe restituire il $ delega oggetto che è assegnato a quella particolare chiave. Successivamente, tenta di chiamare il getCategory metodo su quell'oggetto. Ma la classe Proxy non implementa un tale metodo, quindi come funziona??

Il __chiamata() il metodo magico viene in soccorso! Ogni volta che chiami un metodo che non esiste nella classe, il controllo viene trasferito al __chiamata() metodo magico.

Esploriamolo in dettaglio per capire cosa sta succedendo. Aprire il file di classe Proxy e prestare attenzione a tale metodo.

Il $ chiave contiene il nome della funzione chiamata-getCategory. D'altro canto, $ args contiene argomenti passati al metodo e dovrebbe essere un array di un elemento che contiene l'id della categoria che viene passato.

Quindi, c'è un array $ arg_data che memorizza riferimenti di argomenti. Francamente, non sono sicuro se il codice $ arg instanceof Ref ha senso o no lì. Se qualcuno sa perché è lì, sarei felice di imparare.

Inoltre, cerca di verificare l'esistenza del $ chiave proprietà nel $ delega oggetto, e si traduce in qualcosa di simile.

if (isset ($ this-> getCategory)) 

Ricordiamo che in precedenza abbiamo assegnato tutti i metodi di ModelCatalogCategory classe come proprietà del $ delega oggetto usando a per ciclo continuo. Per tua comodità, incollo nuovamente il codice.

... foreach (get_class_methods ($ class) come $ method) $ proxy -> $ method = $ this-> callback ($ this-> registry, $ route. '/'. $ Method);  ... 

Quindi dovrebbe esserci, e dovrebbe anche restituirci la funzione callable! E infine, chiama quella funzione callable usando il call_user_func_array funzione passando la funzione callable stessa e gli argomenti del metodo.

Ora, dedichiamo la nostra attenzione alla funzione callable definition stessa. Prenderò lo snippet dal richiama metodo definito in Sistema / motore / loader.php.

... function ($ args) use ($ registry, & $ route) statico $ model = array (); $ output = null; // Attiva i pre eventi $ result = $ registry-> get ('event') -> trigger ('model /'. $ Route. '/ Before', array (& $ route, & $ args, & $ output) ); se ($ result) return $ result;  // Salva l'oggetto del modello if (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'modello/' . substr ($ route, 0, strrpos ($ route, '/')). '.Php'; $ class = 'Modello'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ route, 0, strrpos ($ route, '/'))) if if (is_file ($ file)) include_once ($ file); $ model [$ route] = new $ class ($ registry); else throw new \ Exception ('Errore: impossibile caricare il modello'. substr ($ route, 0, strrpos ($ route, '/' )). '!'); $ method = substr ($ route, strrpos ($ route, '/') + 1); $ callable = array ($ model [$ route], $ method); if (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args); else throw new \ Exception ('Errore: impossibile chiamare il modello /'. $ route. '!'); // Attiva il post eventi $ result = $ registry-> get ('event') -> trigger ('model /'. $ route. '/ after', array (& $ route, & $ args e $ output)) if ($ risultato) return $ result; return $ output;; ... 

Essendo una funzione anonima, ha mantenuto i valori sotto forma di $ registro e $ route variabili che sono state passate prima al metodo di callback. In questo caso, il valore di $ route la variabile dovrebbe essere catalogo / categoria / getCategory.

A parte questo, se guardiamo l'importante frammento in quella funzione, esso crea un'istanza del ModelCatalogCategory oggetto e lo memorizza in una statica $ model schieramento.

... // Memorizza l'oggetto modello se (! Isset ($ model [$ route])) $ file = DIR_APPLICATION. 'modello/' . substr ($ route, 0, strrpos ($ route, '/')). '.Php'; $ class = 'Modello'. preg_replace ('/ [^ a-zA-Z0-9] /', ", substr ($ route, 0, strrpos ($ route, '/'))) if if (is_file ($ file)) include_once ($ file); $ model [$ route] = new $ class ($ registry); else throw new \ Exception ('Errore: impossibile caricare il modello'. substr ($ route, 0, strrpos ($ route, '/' )). '!'); ... 

Ed ecco uno snippet che afferra il nome del metodo che deve essere chiamato usando il $ route variabile.

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

Quindi abbiamo un riferimento a un oggetto e un nome di metodo che ci permette di chiamarlo usando il call_user_func_array funzione. Il seguente frammento fa proprio questo!

... if (is_callable ($ callable)) $ output = call_user_func_array ($ callable, $ args);  else throw new \ Exception ('Errore: impossibile chiamare il modello /'. $ route. '!');  ... 

Alla fine del metodo, il risultato risultante viene restituito tramite $ uscita variabile. E sì, questa è un'altra parte della storia!

Ho intenzionalmente ignorato il pre e inviare codice di eventi che consente di sovrascrivere i metodi delle classi core OpenCart. Usando questo, potresti sovrascrivere qualsiasi metodo della classe base e modificarlo in base alle tue necessità. Ma continuiamo a farlo per qualche altro giorno, dato che sarà troppo per adattarsi a un singolo articolo!

Quindi è così che funziona del tutto. Spero che tu debba essere più fiducioso su quelle chiamate stenografiche OpenCart e sul loro funzionamento interno.

Conclusione

Quello che abbiamo appena discusso oggi è uno dei concetti interessanti e ambigui di OpenCart: l'uso del metodo Proxy in un framework per supportare le convenzioni stenografiche per chiamare i metodi del modello. Spero che l'articolo sia stato abbastanza interessante e abbia arricchito la conoscenza del framework OpenCart.

Mi piacerebbe sapere il tuo feedback su questo, e se pensi che dovrei trattare questi argomenti nei miei prossimi articoli, non esitare a lasciare una riga a riguardo!