Core content of Laravel framework: Detailed analysis of Session source code
The content of this article is about the core content of the Laravel framework: a detailed analysis of the Session source code. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Session module source code analysis
Since HTTP was originally an anonymous, stateless request/response protocol, the server processes the request from the client and then sends a response back to the client. In order to provide users with personalized services, modern web applications often need to identify the user in the request or share data between the user's multiple requests. Sessions provide a way to store and share information about a user between multiple requests. Laravel
Handles various built-in Session background drivers through the same readable API.
Drivers supported by Session:
file
- Save Session instorage/framework/sessions
.#cookie
- Session is stored in a secure, encrypted cookie.#database
- Session is stored in a relational database.memcached
/redis
- Sessions are saved in one of the fast and cache-based storage systems.array
- Sessions are saved in PHP arrays and will not be persisted.
In this article, let’s take a detailed look at the implementation principle of the Session
service in Laravel
. The Session
service has Which parts are composed and the role of each part, when it was registered to the service container, when the request enabled the session, and how to extend the driver for the session.
Register Session Service
As mentioned in many previous articles, the service is registered in the service container through the service provider. Laravel will execute config in sequence during the startup phase. The service provider
register method in the
providers array in /app.php
is used to register the services required by the framework, so we can easily think that the session service is also registered at this stage. in the service container.
'providers' => [ /* * Laravel Framework Service Providers... */ ...... Illuminate\Session\SessionServiceProvider::class ...... ],
Sure enough, there is indeed SessionServiceProvider
in providers
Let’s take a look at its source code and see the registration details of the session service
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(); }); } }
in## A total of three services are registered in #SessionServiceProvider:
session
service. After the session service is parsed out, it is a
SessionManagerobject. Its function is to create a session driver and parse the driver when needed (lazy loading). In addition, all method calls to access and update session data are implemented by proxying it to the corresponding session driver.
session.store
Session driver, instance of
Illuminate\Session\Store,
Store classimplements
Illuminate\Contracts\Session\SessionThe contract provides developers with a unified interface to access Session data. The driver accesses
databaseand
redis through differentSessionHandler
Session data in,
memcacheand other different storage media.
StartSession::class
Middleware provides the ability to open the Session at the beginning of the request and write the session identifier to the Cookie before the response is sent to the client. In addition, as a
terminate middleware, after the response is sent to the client, it will save the update to the session data in the request to the storage medium in the
terminate()method.
SessionManager is used to create session driver, which defines various drivers. Creator (method of creating a driver instance) Take a look at its source code to prove that the session driver is created:
<?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; } }Through the source code of
SessionManager, you can see that the driver provides unified services to the outside world. The access interface, and the reason why different types of drives can access different storage media is that the drive accesses the data in the storage media through
SessionHandler, and different
SessionHandler are all implemented uniformly. The
PHP built-in
SessionHandlerInterface interface is installed, so the driver can access data in different session storage media through a unified interface method.
Session facade or
$request->session() to access Session data through
sessionThe service is the
SessionManager object forwarded to the corresponding driver method. We can also see
Laravel in the source code of
Illuminate\Session\Store The session methods used in are all defined here.
Session::get($key); Session::has($key); Session::put($key, $value); Session::pull($key); Session::flash($key, $value); Session::forget($key);The above session methods can be found in the
Illuminate\Session\Store class. Specific method implementations
<?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)); } }Since the driver has a lot of source code, I only leave Some commonly used methods and annotations are provided for key methods. For the complete source code, you can see the source code of the
Illuminate\Session\Store class. Through the source code of the
Store class we can find:
每个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类的使用方法总结(代码)
The above is the detailed content of Core content of Laravel framework: Detailed analysis of Session source code. For more information, please follow other related articles on the PHP Chinese website!

In this era of continuous technological advancement, mastering advanced frameworks is crucial for modern programmers. This article will help you improve your development skills by sharing little-known techniques in the Laravel framework. Known for its elegant syntax and a wide range of features, this article will dig into its powerful features and provide practical tips and tricks to help you create efficient and maintainable web applications.

Laravel and ThinkPHP are both popular PHP frameworks and have their own advantages and disadvantages in development. This article will compare the two in depth, highlighting their architecture, features, and performance differences to help developers make informed choices based on their specific project needs.

Building user login capabilities in Laravel is a crucial task and this article will provide a comprehensive overview covering every critical step from user registration to login verification. We will dive into the power of Laravel’s built-in verification capabilities and guide you through customizing and extending the login process to suit specific needs. By following these step-by-step instructions, you can create a secure and reliable login system that provides a seamless access experience for users of your Laravel application.

In the Laravel framework version selection guide for beginners, this article dives into the version differences of Laravel, designed to assist beginners in making informed choices among many versions. We will focus on the key features of each release, compare their pros and cons, and provide useful advice to help beginners choose the most suitable version of Laravel based on their skill level and project requirements. For beginners, choosing a suitable version of Laravel is crucial because it can significantly impact their learning curve and overall development experience.

The Laravel framework has built-in methods to easily view its version number to meet the different needs of developers. This article will explore these methods, including using the Composer command line tool, accessing .env files, or obtaining version information through PHP code. These methods are essential for maintaining and managing versioning of Laravel applications.

Laravel is a popular PHP-based web application framework that is popular among developers for its elegant syntax and powerful features. Its latest version introduces many improvements and new features designed to improve the development experience and application performance. This article will dive into Laravel's latest approach, focusing on how to leverage these updates to build more powerful and efficient web applications.

Article summary: This article provides detailed step-by-step instructions to guide readers on how to easily install the Laravel framework. Laravel is a powerful PHP framework that speeds up the development process of web applications. This tutorial covers the installation process from system requirements to configuring databases and setting up routing. By following these steps, readers can quickly and efficiently lay a solid foundation for their Laravel project.

Want to learn the Laravel framework, but suffer from no resources or economic pressure? This article provides you with free learning of Laravel, teaching you how to use resources such as online platforms, documents and community forums to lay a solid foundation for your PHP development journey from getting started to master.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

WebStorm Mac version
Useful JavaScript development tools

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

Atom editor mac version download
The most popular open source editor