Laravel Unwrapped Session, Auth et Cache

Au cours des dernières années, Laravel est devenu l’un des cadres les plus importants utilisés par les ingénieurs en logiciel pour créer leurs applications Web. Semblable à la popularité de CodeIgniter à son apogée, Laravel a été félicité pour sa facilité d'utilisation, sa convivialité pour les débutants et son respect des normes de l'industrie..

introduction

Une chose que peu de programmeurs utilisent, cependant, est le système à base de composants de Laravel. Depuis sa conversion en composants composites, Laravel 4 est devenu un système très modulaire, similaire à la verbosité de frameworks plus matures comme Symfony. Ceci est appelé le Éclairer groupe de composants qui, à mon avis, ne constitue pas le framework proprement dit, mais une compilation de bibliothèques qu'un framework peut potentiellement utiliser. Le cadre actuel de Laravel est représenté par l’application de squelette de Laravel (trouvée sur le laravel / laravel Référentiel GitHub) qui utilise ces composants pour construire une application Web.

Dans ce didacticiel, nous allons plonger dans un groupe de ces composants pour apprendre comment ils fonctionnent, comment ils sont utilisés par le framework et comment nous pouvons étendre leurs fonctionnalités..

Le composant de session

Le composant Laravel Session gère les sessions de l'application Web. Il utilise un système basé sur des pilotes appelé Laravel Manager, qui agit à la fois comme une fabrique et une encapsuleuse quel que soit le pilote défini dans le fichier de configuration. A ce jour, le composant Session possède des pilotes pour:

  • fichier - un pilote de session basé sur un fichier où les données de session sont enregistrées dans un fichier crypté.
  • biscuit - un pilote de session basé sur les cookies où les données de session sont cryptées dans les cookies de l'utilisateur.
  • base de données - les données de session sont enregistrées dans la base de données configurée pour l'application.
  • apc - les données de session sont enregistrées dans APC.
  • memcached - les données de session sont enregistrées dans Memcached.
  • redis - les données de session sont enregistrées dans Redis.
  • tableau - les données de session sont enregistrées dans un tableau PHP. Notez que le pilote de session de groupe ne prend pas en charge la persistance et n’est généralement utilisé que dans les commandes de la console..

Les fournisseurs de services

La plupart des utilisateurs de Laravel ne réalisent pas, mais une grande partie de son fonctionnement réside dans ses fournisseurs de services. Ce sont essentiellement des fichiers d'amorçage pour chaque composant et suffisamment abstraits pour que les utilisateurs puissent amorcer n'importe quel composant, de n'importe quelle manière..

Une explication approximative de la façon dont cela fonctionne est ci-dessous:

  1. Le composant Laravel Application est lancé. Il s’agit du principal moteur de l’ensemble de la structure, responsable de la gestion de la requête HTTP, de l’exécution des fournisseurs de services et de la gestion du conteneur de dépendance pour la structure..
  2. Une fois qu'un fournisseur de services est exécuté, ses registre méthode est appelée. Cela nous permet d’instancier le composant que nous voulons..
    • N'oubliez pas que tous les fournisseurs de services ont accès à l'application principale Laravel (via $ this-> app), ce qui permettrait aux fournisseurs de services d'insérer des instances des classes résolues dans le conteneur de dépendance.
  3. Une fois que ces dépendances sont chargées, nous devrions être libres de les utiliser en appelant sur le conteneur, par exemple via le système de façade de Laravel., App :: make.

Pour revenir aux sessions, jetons un coup d’œil sur la SessionServiceProivider:

 / ** * Enregistrez l'instance du gestionnaire de session. * * @retour void * / protected function registerSessionManager () $ this-> app-> bindShared ('session', fonction ($ app) retourne le nouveau SessionManager ($ app););  / ** * Enregistrez l'instance du pilote de session. * * @return void * / protected function registerSessionDriver () $ this-> app-> bindShared ('session.store', function ($ app) // Tout d'abord, nous allons créer le gestionnaire de session qui est responsable du / / Création des différents pilotes de session quand ils sont requis par l'instance d'application // et résolvent-les sur une base de chargement paresseux. $ manager = $ app ['session']; return $ manager-> driver ();) ; 

Ces deux méthodes sont appelées par le registre() une fonction. Le premier, registerSessionManager (), est appelé à enregistrer initialement le SessionManager. Cette classe étend la Directeur que j'ai mentionné en haut. Le deuxième, registerSessionDriver () enregistre un gestionnaire de session pour le gestionnaire, en fonction de ce que nous avons configuré. Cela appelle finalement cette méthode dans le Illuminer \ Support \ Manager classe:

/ ** * Créer une nouvelle instance de pilote. * * @param string $ driver * @return mixed * * @throws \ InvalidArgumentException * / protected function createDriver ($ driver) $ method = 'create'.ucfirst ($ driver).' Driver '; // Nous vérifierons s'il existe une méthode créateur pour le pilote donné. Sinon, nous rechercherons un créateur de pilote personnalisé, ce qui permettra aux développeurs de créer // des pilotes à l'aide de leur propre créateur de pilote personnalisé, Closure, pour le créer. if (isset ($ this-> customCreators [$ driver])) return $ this-> callCustomCreator ($ driver);  elseif (method_exists ($ this, $ method)) return $ this -> $ method ();  jeter new \ InvalidArgumentException ("Driver [$ driver] non supporté."); 

À partir de là, nous pouvons voir que, sur la base du nom du pilote, une méthode spécifique est appelée à partir du fichier de configuration. Donc, si nous l’avons configuré pour utiliser le fichier gestionnaire de session, il appellera cette méthode dans le SessionManager classe:

/ ** * Crée une instance du pilote de session de fichier. * * @return \ Illuminate \ Session \ Store * / fonction protégée createFileDriver () return $ this-> createNativeDriver ();  / ** * Créer une instance du pilote de session de fichier. * * @return \ Illuminate \ Session \ Store * / Fonction protégée createNativeDriver () $ path = $ this-> app ['config'] ['session.files']; return $ this-> buildSession (new FileSessionHandler ($ this-> app ['files'], $ path)); 

La classe de pilotes est ensuite injectée dans un le magasin class, responsable de l'appel des méthodes de session réelles. Cela nous permet en réalité de séparer la mise en œuvre de la SessionHandlerInterface de la SPL dans les pilotes, le le magasin la classe le facilite.

Créer notre propre gestionnaire de session

Créons notre propre gestionnaire de session, un gestionnaire de session MongoDB. Tout d’abord, nous devrons créer un MongoSessionHandler dans une instance de projet Laravel nouvellement installée. (Nous emprunterons énormément auprès de Symfony \ Component \ HttpFoundation \ Session \ Storage \ Handler \ MongoDbSessionHandler):

config = $ config; $ connection_string = 'Mongodb: //'; if (! empty ($ this-> config ['nom d'utilisateur']]) &&! empty ($ this-> config ['mot de passe'])) $ connection_string. = "$ this-> config ['utilisateur'] : $ this-> config ['password'] @ ";  $ connection_string. = "$ this-> config ['hôte']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * @inheritDoc * / fonction publique open ($ savePath, $ sessionName) return true;  / ** * @inheritDoc * / public function close () return true;  / ** * @inheritDoc * / fonction publique read ($ sessionId) $ session_data = $ this-> collection-> findOne (array ('_id' => $ sessionId,)); if (is_null ($ session_data)) return "; else return $ data_session ['session_data'] -> bin; / ** * @inheritDoc * / fonction publique write ($ sessionId, $ data)  $ this-> collection-> update (array ('_id' => $ sessionId), array ('$ set' => array ('session_data' => new MongoBinData ($ data, MongoBinData :: BYTE_ARRAY), 'horodatage' => new MongoDate (),)), array ('upsert' => true, 'multiple' => false)); / ** * @inheritDoc * / fonction publique destroy ($ sessionId) $ this- > collection-> remove (array ('_id' => $ sessionId)); return true; / ** * @inheritDoc * / fonction publique gc ($ à vie) $ time = new MongoDate (time () - $ à vie); $ this-> collection-> remove (array ('horodatage' => array ('$ lt' => $ time),)); return true; 

Vous devriez enregistrer ceci dans le fournisseur / laravel / framework / src / Illuminate / Session dossier. Pour les besoins de ce projet, nous le mettrons ici, mais idéalement, ce fichier devrait se trouver dans son propre espace de noms de bibliothèque..

Ensuite, nous devons nous assurer que le Directeur la classe peut appeler ce pilote. Nous pouvons le faire en utilisant le Manager :: extend méthode. Ouvrir fournisseur / laravel / framework / src / Illuminate / Session / SessionServiceProvider.php et ajoutez le code suivant. Idéalement, nous devrions élargir le fournisseur de services, mais cela sort du cadre de ce tutoriel..

/ ** * Configurer le rappel du pilote Mongo * * * @retour de retour * / fonction publique setupMongoDriver () $ manager = $ this-> app ['session']; $ manager-> extend ('mongo', fonction ($ app) retourne le nouveau MongoSessionHandler (array ('hôte' => $ app ['config']] -> get ('session.mongo.host'), 'nomutilisateur' => $ app ['config'] -> get ('session.mongo.username'), 'password' => $ app ['config'] -> get ('session.mongo.password'), 'base de données' => $ app ['config'] -> get ('session.mongo.database'), 'collection' => $ app ['config'] -> get ('session.mongo.collection'))); ) 

Assurez-vous de mettre à jour le registre() méthode pour appeler cette méthode:

/ ** * Enregistrez le fournisseur de services. * * @return void * / public function register () $ this-> setupDefaultDriver (); $ this-> registerSessionManager (); $ this-> setupMongoDriver (); $ this-> registerSessionDriver (); 

Ensuite, nous devons définir la configuration de la base de données Mongo. Ouvrir app / config / session.php et définissez les paramètres de configuration suivants:

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

Pendant que nous sommes sur ce fichier, nous devrions également mettre à jour le chauffeur configuration en haut:

'driver' => 'mongo'

Maintenant, essayez d’accéder à la page principale (généralement, localhost / un dossier / public). Si cette page se charge sans afficher la WHOOPS page, puis félicitations, nous avons créé avec succès un tout nouveau pilote de session! Testez-le en définissant des données factices sur la session, via Session :: set () puis en le renvoyant via Session :: get ().

Le composant Auth

Le composant Laravel Auth gère l'authentification des utilisateurs pour la structure, ainsi que la gestion des mots de passe. Le composant Laravel a créé ici une interprétation abstraite du système de gestion des utilisateurs typique, utilisable dans la plupart des applications Web, ce qui aide le programmeur à implémenter facilement un système de connexion. Comme le composant Session, il utilise également le gestionnaire Laravel. Actuellement, le composant Auth dispose de pilotes pour:

  • éloquent - cela fait appel à l'ORM intégré de Laravel appelé Éloquent. Il utilise également le pré-fait User.php classe à l'intérieur du des modèles dossier.
  • base de données - ceci utilise la connexion de base de données configurée par défaut. Il utilise un GenericUser classe pour accéder aux données de l'utilisateur.

Comme cela suit la même mise en œuvre que le Session composant, le fournisseur de service est très similaire à ce que nous avons vu en haut:

/ ** * Enregistrez le fournisseur de services. * * @return void * / public function register () $ this-> app-> bindShared ('auth', function ($ app) // Une fois que le service d'authentification a été demandé par le développeur // nous allons définir une variable dans l’application indiquant ceci, ce qui nous aide // à savoir que nous devons définir ultérieurement les cookies en file d'attente dans l'événement after. $ app ['auth.loaded'] = true; renvoie le nouvel AuthManager ($ app);) ; 

Ici, nous pouvons voir que cela crée essentiellement un AuthManager classe qui englobe le pilote que nous utilisons, en plus d’agir en tant qu’usine. À l'intérieur de AuthManager, il crée à nouveau le pilote approprié, enroulé autour d'un Garde classe, qui agit de la même manière que le le magasin classe de Session.

Créer notre propre gestionnaire d'authentification

Comme avant, commençons par créer un MongoUserProvider:

config = $ config; $ connection_string = 'Mongodb: //'; if (! empty ($ this-> config ['nom d'utilisateur']]) &&! empty ($ this-> config ['mot de passe'])) $ connection_string. = "$ this-> config ['utilisateur'] : $ this-> config ['password'] @ ";  $ connection_string. = "$ this-> config ['hôte']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * Récupère un utilisateur par son identifiant unique. * * @param mixed $ identifier * @return \ Illuminate \ Auth \ UserInterface | null * / fonction publique retrieveById ($ identifier) ​​$ user_data = $ this-> collection-> findOne (array ('_id' => $ identifier, )); if (! is_null ($ user_data)) retour new GenericUser ((array) $ user_data);  / ** * Récupérer un utilisateur à l'aide des informations d'identification fournies. * * @param array $ credentials * @return \ Illuminate \ Auth \ UserInterface | null * / fonction publique retrieveByCredentials (array $ credentials) // Essayer de rechercher l'utilisateur en premier, peu importe le mot de passe //, nous le ferons dans le Méthode validateCredentials if (isset ($ credentials ['password']))) unset ($ credentials ['password']));  $ user_data = $ this-> collection-> findOne ($ credentials); if (! is_null ($ user_data)) retour new GenericUser ((array) $ user_data);  / ** * Valider un utilisateur en fonction des informations d'identification fournies. * * @param \ Illuminate \ Auth \ UserInterface $ utilisateur * @param tableau $ informations d'identification * @return bool * / fonction publique validateCredentials (UserInterface $ utilisateur, tableau $ informations d'identification) if (! isset ($ informations d'identification ['mot de passe']) ) return false;  return ($ credentials ['password'] === $ user-> getAuthPassword ()); 

Il est important de noter ici que je ne vérifie pas un mot de passe haché, cela a été fait pour des raisons de simplicité afin de nous permettre de créer plus facilement des données factices et de les tester plus tard. Dans le code de production, vous devez vous assurer de hacher le mot de passe. Vérifiez Illuminate \ Auth \ DatabaseUserProvider classe pour un excellent exemple sur la façon de le faire.

Ensuite, nous devons enregistrer notre rappel de pilote personnalisé sur le AuthManager. Pour ce faire, nous devons mettre à jour les informations du fournisseur de services. registre méthode:

/ ** * Enregistrez le fournisseur de services. * * @return void * / public function register () $ this-> app-> bindShared ('auth', function ($ app) // Une fois que le service d'authentification a été demandé par le développeur // nous allons définir une variable dans l’application nous indiquant // que nous devons définir ultérieurement les cookies en file d'attente dans l'événement after. $ app ['auth.loaded'] = true; $ auth_manager = new AuthManager ($ app); $ auth_manager-> extend ('mongo', fonction ($ app) retourne le nouveau MongoUserProvider (array ('hôte' => $ app ['config'] -> get ('auth.mongo.host'), 'nomutilisateur' => $ app ['config'] -> get ('auth.mongo.username'), 'password' => $ app ['config'] -> get ('auth.mongo.password'), 'base de données' => $ app ['config'] -> get ('auth.mongo.database'), 'collection' => $ app ['config'] -> get ('auth.mongo.collection'))) ); return $ auth_manager;); 

Enfin, nous devons également mettre à jour le auth.php fichier de configuration pour utiliser le pilote Mongo et lui fournir les valeurs de configuration Mongo appropriées:

'driver' => 'mongo',… / ** * Paramètres de la base de données Mongo * / 'mongo' => array ('host' => '127.0.0.1', 'username' => ", 'password' =>" , 'database' => 'laravel', 'collection' => 'laravel_auth_collection')

Le test est un peu plus délicat. Pour ce faire, utilisez la CLI de Mongo DB pour insérer un nouvel utilisateur dans la collection:

mongo> use laravel_auth a basculé sur db laravel_auth> db.laravel_auth_collection.insert (id: 1, email: "[email protected]", mot de passe: "test_password")> db.laravel_auth_collection.find ()> "_id" : ObjectId ("530c609f2caac8c3a8e4814f"), "id" 1, "email": "[email protected]", "mot de passe": "mot_de_passe_test"

Maintenant, testez-le en essayant un Auth :: valider appel de méthode:

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

Cela devrait jeter un bool (true). Si tel est le cas, nous avons créé avec succès notre propre pilote Auth.!

Le composant de cache

Le composant Laravel Cache gère les mécanismes de mise en cache à utiliser dans la structure. Comme les deux composants dont nous avons parlé, il utilise également le gestionnaire Laravel (remarquez-vous un modèle?). Le composant Cache possède des pilotes pour:

  • apc
  • memcached
  • redis
  • fichier - un cache basé sur des fichiers. Les données sont enregistrées dans le app / stockage / cache chemin.
  • base de données - cache base de données. Les données sont enregistrées dans des lignes dans la base de données. Le schéma de base de données est décrit dans la documentation de Laravel.
  • tableau - les données sont "mises en cache" dans un tableau. Gardez à l'esprit que le tableau le cache n'est pas persistant et est effacé à chaque chargement de page.

Etant donné que cela suit la même implémentation que les deux composants dont nous avons parlé, vous pouvez sans risque supposer que le fournisseur de services est assez similaire:

/ ** * Enregistrez le fournisseur de services. * * @return void * / public function register () $ this-> app-> bindShared ('cache', fonction ($ app) retourne un nouveau cacheManager ($ app);); $ this-> app-> bindShared ('cache.store', fonction ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', function () renvoie new MemcachedConnector;); $ this-> registerCommands (); 

le registre() méthode crée ici un CacheManager, cela agit à nouveau comme un emballage et une usine pour les pilotes. Au sein du gestionnaire, le conducteur est entouré Dépôt classe, semblable à la le magasin et Garde Des classes.

Créer notre propre gestionnaire de cache

Créer le MongoStore, qui devrait étendre la Illuminer \ Cache \ StoreInterface:

config = $ config; $ connection_string = 'Mongodb: //'; if (! empty ($ this-> config ['nom d'utilisateur']]) &&! empty ($ this-> config ['mot de passe'])) $ connection_string. = "$ this-> config ['utilisateur'] : $ this-> config ['password'] @ ";  $ connection_string. = "$ this-> config ['hôte']"; $ this-> connection = new Mongo ($ connection_string); $ this-> collection = $ this-> connection-> selectCollection ($ this-> config ['database'], $ this-> config ['collection']);  / ** * Récupère un élément du cache par clé. * * @param chaîne $ key * @return mixed * / fonction publique get ($ key) $ cache_data = $ this-> getObject ($ key); if (! $ cache_data) return null;  return unserialize ($ cache_data ['cache_data']);  / ** * Renvoie l'objet entier au lieu de juste la cache_data * * @param chaîne $ key * @return array | null * / protected function getObject ($ key) $ cache_data = $ this-> collection-> findOne (array ('key' => $ key,)); if (is_null ($ cache_data)) return null;  if (isset ($ cache_data ['expire']) && time ()> = $ cache_data ['expire']) $ this-> oublier ($ key); return null;  return $ cache_data;  / ** * Stocke un élément dans le cache pendant un nombre de minutes donné. * * @param chaîne $ clé * @param mixte $ valeur * @param int $ minutes * @retour void * / fonction publique put ($ key, $ value, $ minutes) $ expiry = $ this-> expiration ($ minutes ) $ this-> collection-> update (array ('key' => $ key), array ('$ set' => array ('cache_data' => serialize ($ value), 'expiry' => $ expiry, ' ttl '=> ($ minutes * 60))), tableau (' upsert '=> vrai,' multiple '=> faux));  / ** * Incrémente la valeur d'un élément dans le cache. * * @param chaîne $ clé * @param mixte $ valeur * @retour void * * @throws \ LogicException * / incrément de fonction public ($ key, $ value = 1) $ cache_data = $ this-> getObject ($ key) ; if (! $ cache_data) $ new_data = array ('cache_data' => sérialiser ($ valeur), 'expiry' => $ this-> expiration (0), 'ttl' => $ this-> expiration (0) )  else $ new_data = array ('cache_data' => sérialiser (unserialize ($ cache_data ['cache_data']) + $ valeur), 'expiry' => $ this-> expiration ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ key), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Décrémente la valeur d'un élément du cache. * * @param chaîne $ clé * @param mixte $ valeur * @retour void * * @throws \ LogicException * / fonction publique décrémentée ($ clé, $ valeur = 1) $ cache_data = $ ceci-> getObject ($ clé) ; if (! $ cache_data) $ new_data = array ('cache_data' => sérialiser ((0 - $ valeur)), 'expiry' => $ this-> expiration (0), 'ttl' => $ this-> expiration (0));  else $ new_data = array ('cache_data' => sérialiser (unserialize ($ cache_data ['cache_data']) - $ valeur), 'expiry' => $ this-> expiration ((int) ($ cache_data ['ttl '] / 60)),' ttl '=> $ cache_data [' ttl ']);  $ this-> collection-> update (array ('key' => $ key), array ('$ set' => $ new_data), array ('upsert' => true, 'multiple' => false)) ;  / ** * Stocke un élément dans le cache indéfiniment. * * @param chaîne $ clé * @param mixte $ valeur * @retour void * / fonction publique pour toujours ($ key, $ valeur) return $ this-> put ($ key, $ valeur, 0);  / ** * Supprimer un élément du cache. * * @param chaine $ key * @retour void * / fonction publique oublier ($ key) $ this-> collection-> remove (array ('key' => $ key));  / ** * Supprimer tous les éléments du cache. * * @return void * / public function flush () $ this-> collection-> remove ();  / ** * Obtenir l'heure d'expiration en fonction des minutes données. * * @param int $ minutes * @return int * / expiration de la fonction protégée ($ minutes) if ($ minutes === 0) retourne 9999999999; temps de retour () + ($ minutes * 60);  / ** * Récupère le préfixe de la clé de cache. * * @return string * / public function getPrefix () return ";

Nous devrons également ajouter à nouveau le rappel Mongo au gestionnaire:

/ ** * Enregistrez le fournisseur de services. * * @return void * / public function register () $ this-> app-> bindShared ('cache', fonction ($ app) $ cache_manager = new CacheManager ($ app); $ cache_manager-> extend ('mongo ', fonction ($ app) retourne le nouveau MongoStore (array (' hôte '=> $ app [' config '] - - get (' cache.mongo.host '),' nomutilisateur '=> $ 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')));); retour $ cache_manager;) ; $ this-> app-> bindShared ('cache.store', fonction ($ app) return $ app ['cache'] -> driver ();); $ this-> app-> bindShared ('memcached.connector', function () renvoie new MemcachedConnector;); $ this-> registerCommands (); 

Enfin, nous devrons mettre à jour le cache.php fichier de configuration:

'driver' => 'mongo',… / ** * Paramètres de la base de données Mongo * / 'mongo' => array ('host' => '127.0.0.1', 'username' => ", 'password' =>" , 'database' => 'laravel', 'collection' => 'laravel_cache_collection')

Maintenant, essayez d'utiliser le Cache :: put () et Cache :: get () méthodes. Si cela est fait correctement, nous devrions pouvoir utiliser MongoDB pour mettre en cache les données!

Conclusion

Dans ce tutoriel, nous avons appris ce qui suit:

  • Le système à base de composants de Laravel appelé Éclairer, qui est utilisé par le framework Laravel.
  • Laravel Service Providers et leur fonctionnement.
  • Le système Manager de Laravel, qui sert à la fois de wrapper et d’usine pour les pilotes.
  • Composants Session, Auth et Cache et comment créer de nouveaux pilotes pour chacun.
  • Bibliothèques Store, Guard et Repository utilisant ces pilotes.

Espérons que cela aide les programmeurs à créer leurs propres pilotes et à étendre les fonctionnalités actuelles du framework Laravel.