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.
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:
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.
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.
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!