Laravel Unwrapped Session, Auth e Cache

Negli ultimi anni, Laravel è diventato uno dei framework più importanti che gli ingegneri del software utilizzano per costruire le loro applicazioni web. Simile alla popolarità che CodeIgniter ha goduto nel suo periodo di massimo splendore, Laravel è stato lodato per la sua facilità d'uso, la cordialità per i principianti e la sua aderenza agli standard del settore.

introduzione

Una cosa, tuttavia, che molti programmatori non sfruttano è il sistema basato su componenti di Laravel. Dalla sua conversione a componenti basati su compositori, Laravel 4 è diventato un sistema molto modulare, simile alla verbosità di framework più maturi come Symfony. Questo è chiamato il Illuminare gruppo di componenti, che a mio parere, non è la struttura vera e propria, ma è una raccolta di librerie che un framework può potenzialmente utilizzare. L'attuale struttura di Laravel è rappresentata dall'applicazione scheletro di Laravel (che si trova sul laravel / laravel Repository GitHub) che utilizza questi componenti per creare un'applicazione web.

In questo tutorial, ci immergeremo in un gruppo di questi componenti, imparando come funzionano, come vengono utilizzati dal framework e come possiamo estendere le loro funzionalità.

Il componente della sessione

Il componente Laravel Session gestisce le sessioni per l'applicazione Web. Fa uso di un sistema basato su driver chiamato Laravel Manager, che funge da factory e wrapper per ogni driver impostato nel file di configurazione. Al momento della stesura, il componente Session ha i driver per:

  • file - un driver di sessione basato su file in cui i dati di sessione vengono salvati in un file crittografato.
  • biscotto - un driver di sessione basato su cookie in cui i dati di sessione sono crittografati nei cookie dell'utente.
  • Banca dati - i dati di sessione vengono salvati in qualsiasi database configurato per l'applicazione.
  • apc - i dati della sessione vengono salvati in APC.
  • memcached - i dati della sessione vengono salvati in Memcached.
  • Redis - i dati della sessione vengono salvati in Redis.
  • schieramento - i dati di sessione vengono salvati in un array PHP. Prendi nota del fatto che il driver di sessione dell'array non supporta la persistenza e solitamente viene utilizzato solo nei comandi della console.

Fornitori di servizi

La maggior parte degli utenti Laravel non si rende conto, ma una grande parte di come funziona Laravel, è all'interno dei suoi fornitori di servizi. Sono essenzialmente file di bootstrap per ciascun componente e sono abbastanza astratti in modo che gli utenti possano eseguire il bootstrap di qualsiasi componente, in qualsiasi modo.

Una spiegazione approssimativa di come funziona è qui sotto:

  1. Il componente dell'applicazione Laravel viene avviato. Questo è il driver principale dell'intero framework, responsabile della gestione della richiesta HTTP, dell'esecuzione dei provider di servizi e dell'agente come contenitore di dipendenza per il framework.
  2. Una volta che un fornitore di servizi viene eseguito, il suo Registrare il metodo è chiamato. Questo ci consente di istanziare qualsiasi componente desideriamo.
    • Tieni presente che tutti i fornitori di servizi hanno accesso all'applicazione principale di Laravel (tramite $ This-> App), che consentirebbe ai provider di servizi di inviare istanze delle classi risolte nel contenitore delle dipendenze.
  3. Una volta caricate queste dipendenze, dovremmo essere liberi di usarle chiamando il container, ad esempio, tramite il sistema di facciata di Laravel, App :: make.

Tornando a Sessioni, diamo una rapida occhiata al SessionServiceProivider:

 / ** * Registrare l'istanza del gestore di sessione. * * @return void * / protected function registerSessionManager () $ this-> app-> bindShared ('session', function ($ app) return new SessionManager ($ app););  / ** * Registrare l'istanza del driver di sessione. * * @return void * / protected function registerSessionDriver () $ this-> app-> bindShared ('session.store', function ($ app) // Innanzitutto, creeremo il session manager che è responsabile per / / creazione dei vari driver di sessione quando sono necessari dall'istanza dell'applicazione // e li risolverà su base lazy load. $ manager = $ app ['session']; return $ manager-> driver ();) ; 

Questi due metodi sono chiamati da Registrare() funzione. Il primo, registerSessionManager (), è chiamato a registrare inizialmente il SessionManager. Questa classe estende il Manager di cui ho parlato sopra. Il secondo, registerSessionDriver () registra un gestore di sessione per il gestore, in base a ciò che abbiamo configurato. Questo alla fine chiama questo metodo nel Illuminare \ Support \ Manager classe:

/ ** * Crea una nuova istanza del driver. * * @param string $ driver * @return mixed * * @throws \ InvalidArgumentException * / funzione protetta createDriver ($ driver) $ method = 'create'.ucfirst ($ driver).' Driver '; // Verificheremo se esiste un metodo creatore per il driver specificato. In caso contrario, // controllerà la creazione di un driver personalizzato, che consente agli sviluppatori di creare i driver // utilizzando il proprio creatore di driver personalizzato Closure per crearlo. if (isset ($ this-> customCreators [$ driver])) return $ this-> callCustomCreator ($ driver);  elseif (method_exists ($ this, $ method)) return $ this -> $ method ();  lancia nuovi \ InvalidArgumentException ("Driver [$ driver] non supportato."); 

Da qui, possiamo vedere che in base al nome del driver, dal file di configurazione, viene chiamato un metodo specifico. Quindi, se lo abbiamo configurato per usare il file gestore di sessioni, chiamerà questo metodo nel SessionManager classe:

/ ** * Creare un'istanza del driver di sessione del file. * * @return \ Illuminate \ Session \ Store * / protected function createFileDriver () return $ this-> createNativeDriver ();  / ** * Creare un'istanza del driver di sessione del file. * * @return \ Illuminate \ Session \ Store * / protected function createNativeDriver () $ path = $ this-> app ['config'] ['session.files']; return $ this-> buildSession (new FileSessionHandler ($ this-> app ['files'], $ path)); 

La classe del conducente viene quindi iniettata in a Memorizzare classe, che è responsabile della chiamata ai metodi di sessione effettivi. Questo ci consente di separare effettivamente l'implementazione di SessionHandlerInterface dal SPL ai driver, il Memorizzare la classe lo facilita.

Creazione del nostro gestore di sessioni personali

Creiamo il nostro Session Handler, un gestore di sessioni MongoDB. Innanzitutto, dovremo creare un MongoSessionHandler all'interno di un'istanza del progetto Laravel appena installata. (Prendiamo in prestito pesantemente da Symfony \ Component \ HttpFoundation \ Session \ Storage \ Handler \ MongoDbSessionHandler) .:

config = $ config; $ connection_string = 'mongodb: //'; if (! empty ($ this-> config ['username']) &&! empty ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * @inheritDoc * / public function open ($ savePath, $ sessionName) return true;  / ** * @inheritDoc * / public function close () return true;  / ** * @inheritDoc * / public function read ($ sessionId) $ session_data = $ this-> collection-> findOne (array ('_id' => $ sessionId,)); if (is_null ($ session_data)) return "; else return $ session_data ['session_data'] -> bin; / ** * @inheritDoc * / public function write ($ sessionId, $ data)  $ this-> collection-> update (array ('_id' => $ sessionId), array ('$ set' => array ('session_data' => nuovo MongoBinData ($ data, MongoBinData :: BYTE_ARRAY), 'timestamp' => new MongoDate (),)), array ('upsert' => true, 'multiple' => false)); / ** * @inheritDoc * / public function destroy ($ sessionId) $ this- > collection-> remove (array ('_id' => $ sessionId)); restituisce true; / ** * @inheritDoc * / public function gc ($ lifetime) $ time = new MongoDate (time () - $ lifetime); $ this-> collection-> remove (array ('timestamp' => array ('$ lt' => $ time),)); return true; 

Dovresti salvare questo nel vendor / laravel / quadro / src / Illuminate / Session cartella. Per gli scopi di questo progetto, lo inseriremo qui, ma idealmente questo file dovrebbe trovarsi all'interno del proprio spazio dei nomi della libreria.

Quindi, dobbiamo assicurarci che il Manager la classe può chiamare questo driver. Possiamo farlo utilizzando il Gestione :: estendere metodo. Aperto vendor / laravel / quadro / src / Illuminate / Session / SessionServiceProvider.php e aggiungi il seguente codice. Idealmente, dovremmo estendere il fornitore di servizi, ma questo non rientra nell'ambito di questo tutorial.

/ ** * Imposta la richiamata di Mongo Driver * * @return void * / public function setupMongoDriver () $ manager = $ this-> app ['session']; $ manager-> extend ('mongo', function ($ app) restituisce nuovo MongoSessionHandler (array ('host' => $ app ['config'] -> get ('session.mongo.host'), 'username' => $ app ['config'] -> get ('session.mongo.username'), 'password' => $ app ['config'] -> get ('session.mongo.password'), 'database' => $ app ['config'] -> get ('session.mongo.database'), 'collection' => $ app ['config'] -> get ('session.mongo.collection'))); ); 

Assicurati di aggiornare il Registrare() metodo per chiamare questo metodo:

/ ** * Registrare il fornitore di servizi. * * @return void * / public function register () $ this-> setupDefaultDriver (); $ This-> registerSessionManager (); $ This-> setupMongoDriver (); $ This-> registerSessionDriver (); 

Successivamente, è necessario definire la configurazione di Mongo DB. Aperto app / config / session.php e definire le seguenti impostazioni di configurazione:

/ ** * Impostazioni Mongo DB * / 'mongo' => array ('host' => '127.0.0.1', 'username' => ", 'password' =>", 'database' => 'laravel', 'collection' => 'laravel_session_collection')

Mentre siamo su questo file, dovremmo anche aggiornare il file autista configurazione in alto:

'driver' => 'mongo'

Ora, prova e accedi alla pagina principale (di solito, localhost / somefolder / pubblico). Se questa pagina viene caricata senza mostrare il WHOOPS pagina, quindi congratulazioni, abbiamo creato con successo un nuovo driver di sessione! Provalo impostando alcuni dati fittizi sulla sessione, via Session :: set () e poi riecheggiando via Session :: get ().

La componente Auth

Il componente Laravel Auth gestisce l'autenticazione dell'utente per il framework, nonché la gestione delle password. Ciò che il componente Laravel ha fatto qui è quello di creare un'interpretazione astratta del tipico sistema di gestione degli utenti che è utilizzabile nella maggior parte delle applicazioni web, il che a sua volta aiuta il programmatore a implementare facilmente un sistema di login. Come il componente Session, fa anche uso di Laravel Manager. Attualmente, il componente Auth ha i driver per:

  • eloquente - questo fa uso dell'ORM integrato di Laravel chiamato Eloquente. Utilizza anche il pre-made user.php classe dentro il Modelli cartella.
  • Banca dati - questo utilizza per impostazione predefinita qualsiasi connessione al database configurata. Fa uso di a GenericUser classe per l'accesso ai dati dell'utente.

Dal momento che questo segue la stessa implementazione del Sessione componente, il fornitore di servizi è molto simile a quello che abbiamo visto in cima:

/ ** * Registrare il fornitore di servizi. * * @return void * / public function register () $ this-> app-> bindShared ('auth', function ($ app) // Una volta che il servizio di autenticazione è stato effettivamente richiesto dallo sviluppatore // imposteremo una variabile nell'applicazione che indica tale. Questo ci aiuta // sapere che abbiamo bisogno di impostare eventuali cookie in coda nell'evento after successivo. $ app ['auth.loaded'] = true; return new AuthManager ($ app);) ; 

Qui, possiamo vedere che fondamentalmente crea un AuthManager classe che avvolge qualsiasi driver che stiamo usando, oltre a fungere da fabbrica per questo. Dentro il AuthManager, crea nuovamente il driver appropriato, avvolto attorno a Guardia classe, che agisce allo stesso modo del Memorizzare classe da Sessione.

Creazione del nostro gestore di autenticazioni

Come prima, iniziamo creando un MongoUserProvider:

config = $ config; $ connection_string = 'mongodb: //'; if (! empty ($ this-> config ['username']) &&! empty ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * Recupera un utente tramite il suo identificativo univoco. * * @param $ identificativo misto * @return \ Illuminate \ Auth \ UserInterface | null * / public function retrieveById ($ identificatore) $ user_data = $ this-> collection-> findOne (array ('_id' => $ identificatore, )); if (! is_null ($ user_data)) return new GenericUser ((array) $ user_data);  / ** * Recupera un utente in base alle credenziali specificate. * * @param array $ credenziali * @return \ Illuminate \ Auth \ UserInterface | null * / funzione pubblica retrieveByCredentials (array $ credenziali) // Tentativo di cercare l'utente per primo indipendentemente dalla password // Lo faremo nel metodo validateCredentials if (isset ($ credentials ['password'])) unset ($ credentials ['password']);  $ user_data = $ this-> collection-> findOne ($ credentials); if (! is_null ($ user_data)) return new GenericUser ((array) $ user_data);  / ** * Convalida un utente rispetto alle credenziali specificate. * * @param \ Illuminate \ Auth \ UserInterface $ utente * @param array $ credenziali * @return bool * / public function validateCredentials (UserInterface $ user, array $ credentials) if (! isset ($ credentials ['password']) ) return false;  return ($ credentials ['password'] === $ user-> getAuthPassword ()); 

È importante tenere presente che non sto controllando una password con hash, questo è stato fatto per semplicità per rendere più semplice da parte nostra la creazione di dati fittizi e testarli successivamente. Nel codice di produzione, è necessario assicurarsi di hash la password. Guarda il Illuminare \ Auth \ DatabaseUserProvider classe per un grande esempio su come farlo.

Successivamente, dobbiamo registrare la nostra callback del driver personalizzato su AuthManager. Per fare ciò, dobbiamo aggiornare il fornitore di servizi Registrare metodo:

/ ** * Registrare il fornitore di servizi. * * @return void * / public function register () $ this-> app-> bindShared ('auth', function ($ app) // Una volta che il servizio di autenticazione è stato effettivamente richiesto dallo sviluppatore // imposteremo una variabile nell'applicazione che indica tale. Questo ci aiuta // sapere che è necessario impostare eventuali cookie in coda nell'evento after successivo. $ app ['auth.loaded'] = true; $ auth_manager = new AuthManager ($ app); $ auth_manager-> extend ('mongo', function ($ app) restituisce nuovo MongoUserProvider (array ('host' => $ app ['config'] -> get ('auth.mongo.host'), 'username' => $ app ['config'] -> get ('auth.mongo.username'), 'password' => $ app ['config'] -> get ('auth.mongo.password'), 'database' => $ app ['config'] -> get ('auth.mongo.database'), 'collection' => $ app ['config'] -> get ('auth.mongo.collection'))); ); return $ auth_manager;); 

Infine, abbiamo anche bisogno di aggiornare il auth.php file di configurazione per utilizzare il driver Mongo, oltre a fornire i valori di configurazione Mongo corretti:

'driver' => 'mongo', ... / ** * Impostazioni Mongo DB * / 'mongo' => array ('host' => '127.0.0.1', 'username' => ", 'password' =>" , 'database' => 'laravel', 'collection' => 'laravel_auth_collection')

Provare questo è un po 'più complicato, per farlo, usa la CLI di Mongo DB per inserire un nuovo utente nella raccolta:

mongo> usa laravel_auth passato a db laravel_auth> db.laravel_auth_collection.insert (id: 1, email: "[email protected]", password: "test_password")> db.laravel_auth_collection.find ()> "_id" : ObjectId ("530c609f2caac8c3a8e4814f"), "id" 1, "email": "[email protected]", "password": "test_password"

Ora provalo provando un Auth :: validate chiamata al metodo:

var_dump (Auth :: validate (array ('email' => '[email protected]', 'password' => 'test_password')));

Questo dovrebbe scaricare a bool (true). Se lo fa, allora abbiamo creato con successo il nostro driver Auth!

Il componente cache

Il componente Laravel Cache gestisce i meccanismi di memorizzazione nella cache per l'utilizzo nel framework. Come entrambi i componenti che abbiamo discusso, fa anche uso di Laravel Manager (stai notando uno schema?). Il componente Cache ha i driver per:

  • apc
  • memcached
  • Redis
  • file - una cache basata su file. I dati vengono salvati nel app / stoccaggio / cache sentiero.
  • Banca dati - cache basata su database. I dati vengono salvati in righe nel database. Lo schema del database è descritto nella documentazione di Laravel.
  • schieramento - i dati sono "memorizzati nella cache" in una matrice. Tieni presente che il schieramento la cache non è persistente e viene cancellata ad ogni caricamento della pagina.

Poiché ciò segue la stessa implementazione di entrambi i componenti discussi, è possibile presumere che il fornitore di servizi sia abbastanza simile:

/ ** * Registrare il fornitore di servizi. * * @return void * / public function register () $ this-> app-> bindShared ('cache', function ($ app) return new CacheManager ($ app);); $ this-> app-> bindShared ('cache.store', function ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', function () return new MemcachedConnector;); $ This-> registerCommands (); 

Il Registrare() il metodo qui crea a CacheManager, che funge ancora da wrapper e factory per i driver. All'interno del manager, avvolge il driver attorno a deposito classe, simile al Memorizzare e Guardia classi.

Creazione del nostro proprio gestore di cache

Crea il MongoStore, che dovrebbe estendere il Illuminare \ Cache \ StoreInterface:

config = $ config; $ connection_string = 'mongodb: //'; if (! empty ($ this-> config ['username']) &&! empty ($ this-> config ['password'])) $ connection_string. = "$ this-> config ['user'] : $ this-> config [ 'password'] @ ";  $ connection_string. = "$ this-> config ['host']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * Recupera un elemento dalla cache per chiave. * * @param stringa $ chiave * @return mista * / funzione pubblica get (chiave $) $ cache_data = $ this-> getObject (chiave $); if (! $ cache_data) return null;  return unserialize ($ cache_data ['cache_data']);  / ** * Restituisce l'intero oggetto invece del solo cache_data * * @param stringa $ chiave * @return array | null * / funzione protetta getObject (chiave $) $ cache_data = $ this-> collection-> findOne (array ('chiave' => $ chiave,)); if (is_null ($ cache_data)) return null;  if (isset ($ cache_data ['expire']) && time ()> = $ cache_data ['expire']) $ this-> forget ($ key); return null;  restituisce $ cache_data;  / ** * Archivia un oggetto nella cache per un dato numero di minuti. * * @param stringa $ chiave * @param mista $ valore * @param int $ minuti * @return void * / funzione pubblica put ($ chiave, $ valore, $ minuti) $ scadenza = $ this-> scadenza ($ minuti ); $ this-> collection-> update (array ('chiave' => $ chiave), array ('$ set' => array ('cache_data' => serialize ($ value), 'expiry' => $ expiry, ' ttl '=> ($ minutes * 60))), array (' upsert '=> true,' multiple '=> false));  / ** * Incrementa il valore di un elemento nella cache. * * @param stringa $ chiave * @param mista $ valore * @return void * * @throws \ LogicException * / incremento funzione pubblica ($ chiave, $ valore = 1) $ cache_data = $ this-> getObject (chiave $) ; if (! $ cache_data) $ new_data = array ('cache_data' => serialize ($ value), 'expiry' => $ this-> expiration (0), 'ttl' => $ this-> expiration (0) );  else $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) + $ value), 'expiry' => $ this-> expiration ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('chiave' => $ chiave), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Decrementa il valore di un elemento nella cache. * * @param stringa $ chiave * @param mista $ valore * @return void * * @throws \ LogicException * / decremento della funzione pubblica ($ chiave, $ valore = 1) $ cache_data = $ this-> getObject (chiave $) ; if (! $ cache_data) $ new_data = array ('cache_data' => serialize ((0 - $ valore)), 'expiry' => $ this-> expiration (0), 'ttl' => $ this-> scadenza (0));  else $ new_data = array ('cache_data' => serialize (unserialize ($ cache_data ['cache_data']) - $ value), 'expiry' => $ this-> expiration ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('chiave' => $ chiave), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Archivia un oggetto nella cache per un tempo indefinito. * * @param string $ key * @param mixed $ value * @return void * / public function forever ($ key, $ value) return $ this-> put ($ key, $ value, 0);  / ** * Rimuovi un elemento dalla cache. * * @param string $ key * @return void * / public function forget ($ key) $ this-> collection-> remove (array ('key' => $ key));  / ** * Rimuovi tutti gli elementi dalla cache. * * @return void * / public function flush () $ this-> collection-> remove ();  / ** * Ottieni il tempo di scadenza in base ai minuti indicati. * * @param int $ minuti * @return int * / scadenza funzione protetta ($ minuti) if ($ minuti === 0) restituisce 9999999999; tempo di ritorno () + ($ minuti * 60);  / ** * Ottieni il prefisso della chiave di cache. * * @return string * / public function getPrefix () return ";

Dovremo inoltre aggiungere nuovamente la richiamata Mongo al gestore:

/ ** * Registrare il fornitore di servizi. * * @return void * / public function register () $ this-> app-> bindShared ('cache', funzione ($ app) $ cache_manager = nuovo CacheManager ($ app); $ cache_manager-> extend ('mongo ', function ($ app) return new MongoStore (array (' host '=> $ app [' config '] -> get (' cache.mongo.host '),' username '=> $ app [' config '] ] -> get ('cache.mongo.username'), 'password' => $ app ['config'] -> get ('cache.mongo.password'), 'database' => $ app ['config'] ] -> get ('cache.mongo.database'), 'collection' => $ app ['config'] -> get ('cache.mongo.collection')));); restituisce $ cache_manager;) ; $ this-> app-> bindShared ('cache.store', function ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', function () return new MemcachedConnector;); $ This-> registerCommands (); 

Infine, dovremo aggiornare il cache.php file di configurazione:

'driver' => 'mongo', ... / ** * Impostazioni Mongo DB * / 'mongo' => array ('host' => '127.0.0.1', 'username' => ", 'password' =>" , 'database' => 'laravel', 'collection' => 'laravel_cache_collection')

Ora, tenta di usare il Cache :: put () e Cache :: get () metodi. Se fatto correttamente, dovremmo essere in grado di usare MongoDB per memorizzare i dati!

Conclusione

In questo tutorial, abbiamo appreso quanto segue:

  • Il sistema basato su componenti di Laravel ha chiamato Illuminare, che viene utilizzato dal framework di Laravel.
  • Laravel Service Provider e un po 'di come funzionano.
  • Il sistema Manager di Laravel, che funge da wrapper e factory per i driver.
  • Componenti di sessione, Auth e Cache e come creare nuovi driver per ciascuno.
  • Librerie Store, Guard e Repository che utilizzano questi driver.

Speriamo che questo aiuti i programmatori a creare i propri driver e ad estendere le funzionalità attuali del framework Laravel.