Laravel フレームワークのコアコンテンツ: セッションソースコードの詳細な分析
この記事の内容は、Laravel フレームワークの核となる内容です。Session のソースコードの詳細な分析です。必要な方は参考にしていただければ幸いです。
セッションモジュールのソースコード分析
HTTP は元々匿名のステートレスなリクエスト/レスポンス プロトコルであるため、サーバーはクライアントからのリクエストを処理し、クライアントにレスポンスを送り返します。ユーザーにパーソナライズされたサービスを提供するために、最新の Web アプリケーションでは、多くの場合、リクエスト内のユーザーを識別したり、ユーザーの複数のリクエスト間でデータを共有したりする必要があります。セッションは、複数のリクエスト間でユーザーに関する情報を保存および共有する方法を提供します。 Laravel
は、同じ読み取り可能な API を通じてさまざまな組み込みセッション バックエンド ドライバーを処理します。 Laravel
通过同一个可读性强的 API 处理各种自带的 Session 后台驱动程序。
Session支持的驱动:
file
- 将 Session 保存在storage/framework/sessions
中。cookie
- Session 保存在安全加密的 Cookie 中。database
- Session 保存在关系型数据库中。memcached
/redis
- Sessions 保存在其中一个快速且基于缓存的存储系统中。array
- Sessions 保存在 PHP 数组中,不会被持久化。
这篇文章我们来详细的看一下Laravel
中Session
服务的实现原理,Session
服务有哪些部分组成以及每部分的角色、它是何时被注册到服务容器的、请求是在何时启用session的以及如何为session扩展驱动。
注册Session服务
在之前的很多文章里都提到过,服务是通过服务提供器注册到服务容器里的,Laravel在启动阶段会依次执行config/app.php
里providers
数组里的服务提供器register
方法来注册框架需要的服务,所以我们很容易想到session服务也是在这个阶段被注册到服务容器里的。
'providers' => [ /* * Laravel Framework Service Providers... */ ...... Illuminate\Session\SessionServiceProvider::class ...... ],
果真在providers
里确实有SessionServiceProvider
我们看一下它的源码,看看session服务的注册细节
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(); }); } }
在SessionServiceProvider
中一共注册了三个服务:
session
服务,session服务解析出来后是一个SessionManager
对象,它的作用是创建session驱动器并且在需要时解析出驱动器(延迟加载),此外一切访问、更新session数据的方法调用都是由它代理给对应的session驱动器来实现的。session.store
Session驱动器,IlluminateSessionStore
的实例,Store类
实现了IlluminateContractsSessionSession
契约向开发者提供了统一的接口来访问Session数据,驱动器通过不同的SessionHandler
来访问database
、redis
、memcache
等不同的存储介质里的session数据。StartSession::class
中间件,提供了在请求开始时打开Session,响应发送给客户端前将session标示符写入到Cookie中,此外作为一个terminate中间件
在响应发送给客户端后它在terminate()
方法中会将请求中对session数据的更新保存到存储介质中去。
创建Session驱动器
上面已经说了SessionManager
是用来创建session驱动器的,它里面定义了各种个样的驱动器创建器(创建驱动器实例的方法) 通过它的源码来看一下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; } }
通过SessionManager
的源码可以看到驱动器对外提供了统一的访问接口,而不同类型的驱动器之所以能访问不同的存储介质是驱动器是通过SessionHandler
来访问存储介质里的数据的,而不同的SessionHandler
统一都实现了PHP
内建的SessionHandlerInterface
接口,所以驱动器能够通过统一的接口方法访问到不同的session存储介质里的数据。
驱动器访问Session 数据
开发者使用Session
门面或者$request->session()
访问Session数据都是通过session
服务即SessionManager
对象转发给对应的驱动器方法的,在IlluminateSessionStore
的源码中我们也能够看到Laravel
里用到的session方法都定义在这里。
Session::get($key); Session::has($key); Session::put($key, $value); Session::pull($key); Session::flash($key, $value); Session::forget($key);
上面这些session方法都能在IlluminateSessionStore
类里找到具体的方法实现
<?php namespace Illuminate\Session; use Closure; use Illuminate\Support\Arr; use Illuminate\Support\Str; use SessionHandlerInterface; use Illuminate\Contracts\Session\Session; class Store implements Session { /** * The session ID. * * @var string */ protected $id; /** * The session name. * * @var string */ protected $name; /** * The session attributes. * * @var array */ protected $attributes = []; /** * The session handler implementation. * * @var \SessionHandlerInterface */ protected $handler; /** * Session store started status. * * @var bool */ protected $started = false; /** * Create a new session instance. * * @param string $name * @param \SessionHandlerInterface $handler * @param string|null $id * @return void */ public function __construct($name, SessionHandlerInterface $handler, $id = null) { $this->setId($id); $this->name = $name; $this->handler = $handler; } /** * 开启session, 通过session handler从存储介质中读出数据暂存在attributes属性里 * * @return bool */ public function start() { $this->loadSession(); if (! $this->has('_token')) { $this->regenerateToken(); } return $this->started = true; } /** * 通过session handler从存储中加载session数据暂存到attributes属性里 * * @return void */ protected function loadSession() { $this->attributes = array_merge($this->attributes, $this->readFromHandler()); } /** * 通过handler从存储中读出session数据 * * @return array */ protected function readFromHandler() { if ($data = $this->handler->read($this->getId())) { $data = @unserialize($this->prepareForUnserialize($data)); if ($data !== false && ! is_null($data) && is_array($data)) { return $data; } } return []; } /** * Prepare the raw string data from the session for unserialization. * * @param string $data * @return string */ protected function prepareForUnserialize($data) { return $data; } /** * 将session数据保存到存储中 * * @return bool */ public function save() { $this->ageFlashData(); $this->handler->write($this->getId(), $this->prepareForStorage( serialize($this->attributes) )); $this->started = false; } /** * Checks if a key is present and not null. * * @param string|array $key * @return bool */ public function has($key) { return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) { return is_null($this->get($key)); }); } /** * Get an item from the session. * * @param string $key * @param mixed $default * @return mixed */ public function get($key, $default = null) { return Arr::get($this->attributes, $key, $default); } /** * Get the value of a given key and then forget it. * * @param string $key * @param string $default * @return mixed */ public function pull($key, $default = null) { return Arr::pull($this->attributes, $key, $default); } /** * Put a key / value pair or array of key / value pairs in the session. * * @param string|array $key * @param mixed $value * @return void */ public function put($key, $value = null) { if (! is_array($key)) { $key = [$key => $value]; } foreach ($key as $arrayKey => $arrayValue) { Arr::set($this->attributes, $arrayKey, $arrayValue); } } /** * Flash a key / value pair to the session. * * @param string $key * @param mixed $value * @return void */ public function flash(string $key, $value = true) { $this->put($key, $value); $this->push('_flash.new', $key); $this->removeFromOldFlashData([$key]); } /** * Remove one or many items from the session. * * @param string|array $keys * @return void */ public function forget($keys) { Arr::forget($this->attributes, $keys); } /** * Remove all of the items from the session. * * @return void */ public function flush() { $this->attributes = []; } /** * Determine if the session has been started. * * @return bool */ public function isStarted() { return $this->started; } /** * Get the name of the session. * * @return string */ public function getName() { return $this->name; } /** * Set the name of the session. * * @param string $name * @return void */ public function setName($name) { $this->name = $name; } /** * Get the current session ID. * * @return string */ public function getId() { return $this->id; } /** * Set the session ID. * * @param string $id * @return void */ public function setId($id) { $this->id = $this->isValidId($id) ? $id : $this->generateSessionId(); } /** * Determine if this is a valid session ID. * * @param string $id * @return bool */ public function isValidId($id) { return is_string($id) && ctype_alnum($id) && strlen($id) === 40; } /** * Get a new, random session ID. * * @return string */ protected function generateSessionId() { return Str::random(40); } /** * Set the existence of the session on the handler if applicable. * * @param bool $value * @return void */ public function setExists($value) { if ($this->handler instanceof ExistenceAwareInterface) { $this->handler->setExists($value); } } /** * Get the CSRF token value. * * @return string */ public function token() { return $this->get('_token'); } /** * Regenerate the CSRF token value. * * @return void */ public function regenerateToken() { $this->put('_token', Str::random(40)); } }
由于驱动器的源码比较多,我只留下一些常用和方法,并对关键的方法做了注解,完整源码可以去看IlluminateSessionStore
类的源码。 通过Store
- 🎜
file
- セッションをstorage/framework/sessions
に保存します。 🎜 - 🎜
cookie
- セッションは安全で暗号化された Cookie に保存されます。 🎜 - 🎜
database
- セッションはリレーショナル データベースに保存されます。 🎜 - 🎜
memcached
/redis
- セッションは、高速なキャッシュベースのストレージ システムの 1 つに保存されます。 🎜 - 🎜
array
- セッションは PHP 配列に保存され、永続化されません。 🎜
Laravel
の Session
サービスの実装原理と、Session を詳しく見てみましょう
サービス コンポーネントと各部分の役割、サービス コンテナーにいつ登録されたか、リクエストによってセッションが有効になった時期、およびセッション用のドライバーを拡張する方法。 🎜セッションサービスの登録
🎜 これまでの多くの記事で述べたように、サービスはサービスプロバイダーを通じてサービスコンテナに登録され、サービスの起動フェーズ中にconfig/app が順番に実行されます。 php
の providers
配列の Provider register
メソッドは、フレームワークに必要なサービスの登録に使用されるため、セッション サービスも登録されていると簡単に考えることができます。この段階ではサービスコンテナ内にあります。 🎜<?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; } }
providers
には SessionServiceProvider
があります。そのソース コードを見て、 内のセッション サービスの登録詳細を見てみましょう🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><?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) {}
}</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜。 >SessionServiceProvider
合計 3 つのサービスが Zhongyi に登録されました: 🎜- 🎜
session
サービス セッション サービスが解析された後。これはSessionManager
オブジェクトであり、その機能はセッション ドライバーを作成し、必要に応じてドライバーを解析することです (遅延読み込み)。さらに、セッション データにアクセスして更新するすべてのメソッド呼び出しはプロキシによって実装されます。それを対応するセッションドライバーに渡します。 🎜 - 🎜
session.store
セッション ドライバー、IlluminateSessionStore
のインスタンス、Store クラス
はIlluminateContractsSessionSession
を実装します。 > code> コントラクトは、セッション データにアクセスするための統合インターフェイスを開発者に提供します。ドライバーは、さまざまなSessionHandler
を通じてdatabase
、redis
、およびにアクセスします。 > memcache
など、さまざまなストレージ メディア内のセッション データ。 🎜 - 🎜
StartSession::class
ミドルウェア。リクエストの開始時にセッションを開き、レスポンスが送信される前にセッション識別子を Cookie に書き込む機能を提供します。さらに、クライアントは、terminate ミドルウェア
として、応答の送信後にterminate()
メソッドのストレージ メディアへのリクエスト内のセッション データの更新を保存します。クライアントに。 🎜
セッション ドライバーの作成
🎜 前述したように、SessionManager
は、さまざまなドライバー Creator (ドライバーの作成方法) を定義するセッション ドライバーの作成に使用されます。ドライバー インスタンス) ソース コードを見て、セッション ドライバーが作成されていることを確認します。 🎜<?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
のソース コードを通して、ドライバーが外部への統合アクセス インターフェイスを提供していることがわかります。異なるタイプのドライブが異なるストレージ メディアにアクセスできる理由は、ドライブが SessionHandler
を通じてストレージ メディア内のデータにアクセスし、異なる SessionHandler
が統一されているためです。 >PHP には SessionHandlerInterface
インターフェイスが組み込まれているため、ドライバーは統合インターフェイス メソッドを通じてさまざまなセッション ストレージ メディアのデータにアクセスできます。 🎜ドライバーはセッション データにアクセスします
🎜開発者は、Session
ファサードまたは $request->session()
を使用して、 を通じてセッション データにアクセスします。 session
サービスは、対応するドライバー メソッドに転送される SessionManager
オブジェクトであり、Laravel
の IlluminateSessionStore
のソース コードでも確認できます。 > 使用されるセッション メソッドはすべてここで定義されています。 🎜rrreee🎜上記のセッション メソッドは IlluminateSessionStore
クラスで実装できます。🎜rrreee🎜 ドライバーには多くのソース コードがあるため、いくつかの一般的なメソッドとメソッドのみを残し、主要なメソッドを実行します。注釈を理解すると、IlluminateSessionStore
クラスのソース コードで完全なソース コードを確認できます。 Store
クラスのソース コードから次のことがわかります: 🎜每个session数据里都会有一个
_token
数据来做CSRF
防范。Session开启后会将session数据从存储中读出暂存到attributes属性。
驱动器提供给应用操作session数据的方法都是直接操作的attributes属性里的数据。
同时也会产生一些疑问,在平时开发时我们并没有主动的去开启和保存session,数据是怎么加载和持久化的?通过session在用户的请求间共享数据是需要在客户端cookie存储一个session id
的,这个cookie又是在哪里设置的?
上面的两个问题给出的解决方案是最开始说的第三个服务StartSession
中间件
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类的使用方法总结(代码)
以上がLaravel フレームワークのコアコンテンツ: セッションソースコードの詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Laravelの電子メールの送信が失敗したときに戻りコードを取得する方法。 Laravelを使用してアプリケーションを開発する場合、検証コードを送信する必要がある状況に遭遇することがよくあります。そして実際には...

LaravelスケジュールタスクRAN RANSPONSIVEトラブルシューティングRALAVELのスケジュールタスクスケジューリングを使用すると、多くの開発者がこの問題に遭遇します。スケジュール:実行...

Laravelの電子メールの検証コードの送信の障害を処理する方法は、Laravelを使用することです...

DCATを使用するときにDCATADMIN(Laravel-Admin)にデータを追加するためにカスタムクリックのテーブル関数を実装する方法...

Laravel FrameworkでRedis接続の共有の影響とLaravelフレームワークとRedisを使用する際のメソッドを選択すると、開発者は問題に遭遇する可能性があります。

LaravelEloquentモデルの検索:データベースデータを簡単に取得するEloquentormは、データベースを操作するための簡潔で理解しやすい方法を提供します。この記事では、さまざまな雄弁なモデル検索手法を詳細に紹介して、データベースからのデータを効率的に取得するのに役立ちます。 1.すべてのレコードを取得します。 ALL()メソッドを使用して、データベーステーブルですべてのレコードを取得します:useapp \ models \ post; $ post = post :: all();これにより、コレクションが返されます。 Foreach Loopまたはその他の収集方法を使用してデータにアクセスできます。

Laravel Multi-Tenant拡張機能パッケージStancl/Tenancyのカスタムテナントデータベース接続Laravel Multi-Tenant ExtensionパッケージStancl/Tenancyを使用したマルチテナントアプリケーションを構築する際の...

700万のレコードを効率的に処理し、地理空間技術を使用したインタラクティブマップを作成します。この記事では、LaravelとMySQLを使用して700万を超えるレコードを効率的に処理し、それらをインタラクティブなマップの視覚化に変換する方法について説明します。最初の課題プロジェクトの要件:MySQLデータベースに700万のレコードを使用して貴重な洞察を抽出します。多くの人は最初に言語をプログラミングすることを検討しますが、データベース自体を無視します。ニーズを満たすことができますか?データ移行または構造調整は必要ですか? MySQLはこのような大きなデータ負荷に耐えることができますか?予備分析:キーフィルターとプロパティを特定する必要があります。分析後、ソリューションに関連している属性はわずかであることがわかりました。フィルターの実現可能性を確認し、検索を最適化するためにいくつかの制限を設定しました。都市に基づくマップ検索
