Der Inhalt dieses Artikels befasst sich mit dem Kerninhalt des Laravel-Frameworks: Eine detaillierte Analyse des Session-Quellcodes hat einen gewissen Referenzwert. Ich hoffe, er wird für Sie hilfreich sein.
Analyse des Quellcodes des Sitzungsmoduls
Da HTTP ursprünglich ein anonymes, zustandsloses Anforderungs-/Antwortprotokoll war, verarbeitet der Server die Anfrage vom Client und sendet dann eine Antwort an den Client zurück. Um Benutzern personalisierte Dienste bereitzustellen, müssen moderne Webanwendungen häufig den Benutzer in der Anfrage identifizieren oder Daten zwischen mehreren Anfragen des Benutzers austauschen. Sitzungen bieten eine Möglichkeit, Informationen über einen Benutzer zwischen mehreren Anfragen zu speichern und auszutauschen. Laravel
Behandeln Sie verschiedene integrierte Sitzungshintergrundtreiber über dieselbe lesbare API.
Von Sitzung unterstützte Treiber:
file
– Sitzung speichern in storage/framework/sessions
.
cookie
– Die Sitzung wird in einem sicheren und verschlüsselten Cookie gespeichert.
database
– Sitzung wird in einer relationalen Datenbank gespeichert.
memcached
/ redis
- Sitzungen werden in einem der schnellen und Cache-basierten Speichersysteme gespeichert.
array
– Sitzungen werden in PHP-Arrays gespeichert und nicht dauerhaft gespeichert.
In diesem Artikel werfen wir einen detaillierten Blick auf das Implementierungsprinzip des Laravel
-Dienstes in Session
, aus welchen Teilen der Session
-Dienst besteht und welche Rolle er spielt Jeder Teil und was ist das? Wann wurde die Anfrage im Service-Container registriert, wann wurde die Sitzung aktiviert und wie kann der Treiber für die Sitzung erweitert werden?
Wie in vielen vorherigen Artikeln erwähnt, wird der Dienst über den Dienstanbieter config/app.php
nacheinander während der Startphase Die Dienstanbietermethode providers
im Array wird verwendet, um die vom Framework benötigten Dienste zu registrieren, sodass wir leicht annehmen können, dass der Sitzungsdienst zu diesem Zeitpunkt auch im Dienstcontainer registriert ist. register
'providers' => [ /* * Laravel Framework Service Providers... */ ...... Illuminate\Session\SessionServiceProvider::class ...... ],
in providers
. Schauen wir uns den Quellcode an und sehen uns die Registrierungsdetails des Sitzungsdienstes an von drei Diensten im SessionServiceProvider
SessionServiceProvider
session
SessionManager
-Vertrag, um Entwicklern eine einheitliche Schnittstelle für den Zugriff auf Sitzungsdaten bereitzustellen session.store
um auf Sitzungsdaten in verschiedenen Speichermedien wie IlluminateSessionStore
, Store类
, IlluminateContractsSessionSession
usw. zuzugreifen. SessionHandler
database
redis
memcache
StartSession::class
terminate中间件
terminate()
Sitzungstreiber erstellen
namespace Illuminate\Session; use Illuminate\Support\ServiceProvider; use Illuminate\Session\Middleware\StartSession; class SessionServiceProvider extends ServiceProvider { /** * Register the service provider. * * @return void */ public function register() { $this->registerSessionManager(); $this->registerSessionDriver(); $this->app->singleton(StartSession::class); } /** * Register the session manager instance. * * @return void */ protected function registerSessionManager() { $this->app->singleton('session', function ($app) { return new SessionManager($app); }); } /** * Register the session driver instance. * * @return void */ protected function registerSessionDriver() { $this->app->singleton('session.store', function ($app) { // First, we will create the session manager which is responsible for the // creation of the various session drivers when they are needed by the // application instance, and will resolve them on a lazy load basis. return $app->make('session')->driver(); }); } }
können Sie sehen, dass der Treiber eine einheitliche Zugriffsschnittstelle zur Außenwelt bereitstellt und verschiedene Arten von Der Grund, warum das Das Laufwerk kann auf verschiedene Speichermedien zugreifen. Das bedeutet, dass das Laufwerk über SessionManager
auf die Daten im Speichermedium zugreift und verschiedene
-Schnittstelle SessionManager
einheitlich implementieren, sodass das Laufwerk über einheitliche Schnittstellenmethoden zugreifen kann Daten in verschiedenen Sitzungsspeichermedien. SessionHandler
SessionHandler
Treiber greift auf Sitzungsdaten zu PHP
SessionHandlerInterface
Entwickler, die die
können wir auch sehen, dass die in Session
verwendeten Sitzungsmethoden hier definiert sind. $request->session()
<?php namespace Illuminate\Session; use Illuminate\Support\Manager; class SessionManager extends Manager { /** * 调用自定义驱动创建器 (通过Session::extend注册的) * * @param string $driver * @return mixed */ protected function callCustomCreator($driver) { return $this->buildSession(parent::callCustomCreator($driver)); } /** * 创建数组类型的session驱动器(不会持久化) * * @return \Illuminate\Session\Store */ protected function createArrayDriver() { return $this->buildSession(new NullSessionHandler); } /** * 创建Cookie session驱动器 * * @return \Illuminate\Session\Store */ protected function createCookieDriver() { return $this->buildSession(new CookieSessionHandler( $this->app['cookie'], $this->app['config']['session.lifetime'] )); } /** * 创建文件session驱动器 * * @return \Illuminate\Session\Store */ protected function createFileDriver() { return $this->createNativeDriver(); } /** * 创建文件session驱动器 * * @return \Illuminate\Session\Store */ protected function createNativeDriver() { $lifetime = $this->app['config']['session.lifetime']; return $this->buildSession(new FileSessionHandler( $this->app['files'], $this->app['config']['session.files'], $lifetime )); } /** * 创建Database型的session驱动器 * * @return \Illuminate\Session\Store */ protected function createDatabaseDriver() { $table = $this->app['config']['session.table']; $lifetime = $this->app['config']['session.lifetime']; return $this->buildSession(new DatabaseSessionHandler( $this->getDatabaseConnection(), $table, $lifetime, $this->app )); } /** * Get the database connection for the database driver. * * @return \Illuminate\Database\Connection */ protected function getDatabaseConnection() { $connection = $this->app['config']['session.connection']; return $this->app['db']->connection($connection); } /** * Create an instance of the APC session driver. * * @return \Illuminate\Session\Store */ protected function createApcDriver() { return $this->createCacheBased('apc'); } /** * 创建memcache session驱动器 * * @return \Illuminate\Session\Store */ protected function createMemcachedDriver() { return $this->createCacheBased('memcached'); } /** * 创建redis session驱动器 * * @return \Illuminate\Session\Store */ protected function createRedisDriver() { $handler = $this->createCacheHandler('redis'); $handler->getCache()->getStore()->setConnection( $this->app['config']['session.connection'] ); return $this->buildSession($handler); } /** * 创建基于Cache的session驱动器 (创建memcache、apc驱动器时都会调用这个方法) * * @param string $driver * @return \Illuminate\Session\Store */ protected function createCacheBased($driver) { return $this->buildSession($this->createCacheHandler($driver)); } /** * 创建基于Cache的session handler * * @param string $driver * @return \Illuminate\Session\CacheBasedSessionHandler */ protected function createCacheHandler($driver) { $store = $this->app['config']->get('session.store') ?: $driver; return new CacheBasedSessionHandler( clone $this->app['cache']->store($store), $this->app['config']['session.lifetime'] ); } /** * 构建session驱动器 * * @param \SessionHandlerInterface $handler * @return \Illuminate\Session\Store */ protected function buildSession($handler) { if ($this->app['config']['session.encrypt']) { return $this->buildEncryptedSession($handler); } return new Store($this->app['config']['session.cookie'], $handler); } /** * 构建加密的Session驱动器 * * @param \SessionHandlerInterface $handler * @return \Illuminate\Session\EncryptedStore */ protected function buildEncryptedSession($handler) { return new EncryptedStore( $this->app['config']['session.cookie'], $handler, $this->app['encrypter'] ); } /** * 获取config/session.php里的配置 * * @return array */ public function getSessionConfig() { return $this->app['config']['session']; } /** * 获取配置里的session驱动器名称 * * @return string */ public function getDefaultDriver() { return $this->app['config']['session.driver']; } /** * 设置配置里的session名称 * * @param string $name * @return void */ public function setDefaultDriver($name) { $this->app['config']['session.driver'] = $name; } }
session
Die oben genannten Sitzungsmethoden können auf bestimmte Weise in der SessionManager
-Klasse implementiert werdenIlluminateSessionStore
Session::get($key); Session::has($key); Session::put($key, $value); Session::pull($key); Session::flash($key, $value); Session::forget($key);
Laravel
Da der Treiber viel Quellcode hat, lasse ich nur einige häufig verwendete Methoden und Methoden übrig, und der Schlüssel Die Methode ist mit Anmerkungen versehen, und der vollständige Quellcode ist im Quellcode der Klasse zu finden. Durch den Quellcode der Klasse können wir Folgendes finden: IlluminateSessionStore
每个session数据里都会有一个_token
数据来做CSRF
防范。
Session开启后会将session数据从存储中读出暂存到attributes属性。
驱动器提供给应用操作session数据的方法都是直接操作的attributes属性里的数据。
同时也会产生一些疑问,在平时开发时我们并没有主动的去开启和保存session,数据是怎么加载和持久化的?通过session在用户的请求间共享数据是需要在客户端cookie存储一个session id
的,这个cookie又是在哪里设置的?
上面的两个问题给出的解决方案是最开始说的第三个服务StartSession
中间件
<?php namespace Illuminate\Session\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Session\SessionManager; use Illuminate\Contracts\Session\Session; use Illuminate\Session\CookieSessionHandler; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Response; class StartSession { /** * The session manager. * * @var \Illuminate\Session\SessionManager */ protected $manager; /** * Indicates if the session was handled for the current request. * * @var bool */ protected $sessionHandled = false; /** * Create a new session middleware. * * @param \Illuminate\Session\SessionManager $manager * @return void */ public function __construct(SessionManager $manager) { $this->manager = $manager; } /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $this->sessionHandled = true; // If a session driver has been configured, we will need to start the session here // so that the data is ready for an application. Note that the Laravel sessions // do not make use of PHP "native" sessions in any way since they are crappy. if ($this->sessionConfigured()) { $request->setLaravelSession( $session = $this->startSession($request) ); $this->collectGarbage($session); } $response = $next($request); // Again, if the session has been configured we will need to close out the session // so that the attributes may be persisted to some storage medium. We will also // add the session identifier cookie to the application response headers now. if ($this->sessionConfigured()) { $this->storeCurrentUrl($request, $session); $this->addCookieToResponse($response, $session); } return $response; } /** * Perform any final actions for the request lifecycle. * * @param \Illuminate\Http\Request $request * @param \Symfony\Component\HttpFoundation\Response $response * @return void */ public function terminate($request, $response) { if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions()) { $this->manager->driver()->save(); } } /** * Start the session for the given request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Contracts\Session\Session */ protected function startSession(Request $request) { return tap($this->getSession($request), function ($session) use ($request) { $session->setRequestOnHandler($request); $session->start(); }); } /** * Add the session cookie to the application response. * * @param \Symfony\Component\HttpFoundation\Response $response * @param \Illuminate\Contracts\Session\Session $session * @return void */ protected function addCookieToResponse(Response $response, Session $session) { if ($this->usingCookieSessions()) { //将session数据保存到cookie中,cookie名是本条session数据的ID标识符 $this->manager->driver()->save(); } if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) { //将本条session的ID标识符保存到cookie中,cookie名是session配置文件里设置的cookie名 $response->headers->setCookie(new Cookie( $session->getName(), $session->getId(), $this->getCookieExpirationDate(), $config['path'], $config['domain'], $config['secure'] ?? false, $config['http_only'] ?? true, false, $config['same_site'] ?? null )); } } /** * Determine if the configured session driver is persistent. * * @param array|null $config * @return bool */ protected function sessionIsPersistent(array $config = null) { $config = $config ?: $this->manager->getSessionConfig(); return ! in_array($config['driver'], [null, 'array']); } /** * Determine if the session is using cookie sessions. * * @return bool */ protected function usingCookieSessions() { if ($this->sessionConfigured()) { return $this->manager->driver()->getHandler() instanceof CookieSessionHandler; } return false; } }
同样的我只保留了最关键的代码,可以看到中间件在请求进来时会先进行session start
操作,然后在响应返回给客户端前将session id
设置到了cookie响应头里面, cookie的名称是由config/session.php
里的cookie
配置项设置的,值是本条session的ID标识符。与此同时如果session驱动器用的是CookieSessionHandler
还会将session数据保存到cookie里cookie的名字是本条session的ID标示符(呃, 有点绕,其实就是把存在redis
里的那些session数据以ID为cookie名存到cookie里了, 值是JSON
格式化的session数据)。
最后在响应发送完后,在terminate
方法里会判断驱动器用的如果不是CookieSessionHandler
,那么就调用一次$this->manager->driver()->save();
将session数据持久化到存储中 (我现在还没有搞清楚为什么不统一在这里进行持久化,可能看完Cookie服务的源码就清楚了)。
关于添加自定义驱动,官方文档给出了一个例子,MongoHandler
必须实现统一的SessionHandlerInterface
接口里的方法:
<?php namespace App\Extensions; class MongoHandler implements SessionHandlerInterface { public function open($savePath, $sessionName) {} public function close() {} public function read($sessionId) {} public function write($sessionId, $data) {} public function destroy($sessionId) {} public function gc($lifetime) {} }
定义完驱动后在AppServiceProvider
里注册一下:
<?php namespace App\Providers; use App\Extensions\MongoSessionStore; use Illuminate\Support\Facades\Session; use Illuminate\Support\ServiceProvider; class SessionServiceProvider extends ServiceProvider { /** * 执行注册后引导服务。 * * @return void */ public function boot() { Session::extend('mongo', function ($app) { // Return implementation of SessionHandlerInterface... return new MongoSessionStore; }); } }
这样在用SessionManager
的driver
方法创建mongo
类型的驱动器的时候就会调用callCustomCreator
方法去创建mongo
类型的Session驱动器了。
相关推荐:
Laravel中collection类的使用方法总结(代码)
Das obige ist der detaillierte Inhalt vonKerninhalt des Laravel-Frameworks: Detaillierte Analyse des Session-Quellcodes. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!