>  기사  >  PHP 프레임워크  >  Laravel 프레임워크의 핵심 내용: Session 소스코드 상세 분석

Laravel 프레임워크의 핵심 내용: Session 소스코드 상세 분석

不言
不言원래의
2018-08-14 11:16:162284검색

이 글의 내용은 Laravel 프레임워크의 핵심 내용입니다: Session 소스 코드에 대한 상세한 분석입니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다. .

세션 모듈 소스 코드 분석

HTTP는 원래 익명의 상태 비저장 요청/응답 프로토콜이므로 서버는 클라이언트의 요청을 처리한 다음 다시 클라이언트로 응답을 보냅니다. 고객. 사용자에게 개인화된 서비스를 제공하기 위해 최신 웹 애플리케이션은 요청에서 사용자를 식별하거나 사용자의 여러 요청 간에 데이터를 공유해야 하는 경우가 많습니다. 세션은 여러 요청 간에 사용자에 대한 정보를 저장하고 공유하는 방법을 제공합니다. Laravel은 읽기 가능한 동일한 API를 통해 다양한 내장 세션 백엔드 드라이버를 처리합니다. Laravel 通过同一个可读性强的 API 处理各种自带的 Session 后台驱动程序。

Session支持的驱动:

  • file - 将 Session 保存在 storage/framework/sessions 中。

  • cookie - Session 保存在安全加密的 Cookie 中。

  • database - Session 保存在关系型数据库中。

  • memcached / redis - Sessions 保存在其中一个快速且基于缓存的存储系统中。

  • array - Sessions 保存在 PHP 数组中,不会被持久化。

这篇文章我们来详细的看一下LaravelSession服务的实现原理,Session服务有哪些部分组成以及每部分的角色、它是何时被注册到服务容器的、请求是在何时启用session的以及如何为session扩展驱动。

注册Session服务

在之前的很多文章里都提到过,服务是通过服务提供器注册到服务容器里的,Laravel在启动阶段会依次执行config/app.phpproviders数组里的服务提供器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来访问databaseredismemcache等不同的存储介质里的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 - 스토리지/프레임워크/세션. #🎜🎜#
  • #🎜🎜#쿠키 - 세션은 안전하고 암호화된 쿠키에 저장됩니다. #🎜🎜#
  • #🎜🎜#데이터베이스 - 세션이 관계형 데이터베이스에 저장됩니다. #🎜🎜#
  • #🎜🎜#memcached / redis - 세션은 빠른 캐시 기반 스토리지 시스템 중 하나에 저장됩니다. #🎜🎜#
  • #🎜🎜#배열 - 세션은 PHP 배열에 저장되며 지속되지 않습니다. #🎜🎜#
#🎜🎜#이 글에서는 Laravel에서 Session 서비스의 구현 원리를 자세히 살펴보겠습니다. , 세션 서비스는 어떤 부분으로 구성되어 있으며 각 부분의 역할, 서비스 컨테이너에 등록된 시기, 요청이 세션을 활성화한 시기, 세션에 대한 드라이버를 확장하는 방법은 무엇입니까? #🎜🎜#

세션 서비스 등록

#🎜🎜#이전의 많은 기사에서 언급했듯이 서비스는 서비스 공급자를 통해 서비스 컨테이너에 등록되며 Laravel은 시작 단계에서 이를 순차적으로 실행합니다config/app.php의 providers 배열에 있는 서비스 공급자 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가 있습니다. 소스 코드를 살펴보고 세션 서비스의 등록 세부정보를 살펴보겠습니다. #🎜🎜#
<?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) {}
}
# 🎜🎜#SessionServiceProvider에는 총 3개의 서비스가 등록되어 있습니다: #🎜🎜#
  • #🎜🎜#< code>sessionService는 세션 서비스가 구문 분석된 후 SessionManager 개체입니다. 해당 기능은 세션 드라이버를 생성하고 필요할 때 드라이버를 구문 분석하는 것입니다(지연 로딩). 또한 세션 데이터에 액세스하고 업데이트하기 위한 모든 메서드 호출은 이를 해당 세션 드라이버에 프록시하여 구현됩니다. #🎜🎜#
  • #🎜🎜#session.store 세션 드라이버, IlluminateSessionStore의 인스턴스, Store 클래스로 구현됨 IlluminateContractsSessionSession 계약은 개발자에게 세션 데이터에 액세스할 수 있는 통합 인터페이스를 제공합니다. 드라이버는 다양한 SessionHandler 세션 데이터를 통해 databaseredis<에 액세스합니다. /code> 및 <code>memcache와 같은 다른 저장 매체에 있습니다. #🎜🎜#
  • #🎜🎜#StartSession::class 미들웨어는 요청 시작 시 세션을 열고 응답 앞에 세션 식별자를 쓰는 기능을 제공합니다. 또한 종료 미들웨어로 클라이언트에 응답이 전송된 후 요청의 세션 데이터에 대한 업데이트를 저장소에 저장합니다. terminate() 메소드를 사용하여 매체로 이동하세요. #🎜🎜#

세션 드라이버 생성

#🎜🎜#위에서 언급했듯이 SessionManager는 다양한 드라이버 생성자를 정의하는 세션 드라이버를 생성하는 데 사용됩니다. (드라이버 인스턴스 생성 방법) 세션 드라이버가 생성되었는지 확인하려면 소스 코드를 살펴보세요. #🎜🎜#
<?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(&#39;mongo&#39;, function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionStore;
        });
    }
}
#🎜🎜#Through SessionManager 소스 코드에서 확인할 수 있습니다. 드라이버는 외부 세계에 대한 통합 액세스 인터페이스를 제공합니다. 다양한 유형의 드라이버가 서로 다른 저장 매체에 액세스할 수 있는 이유는 드라이버가 SessionHandler를 통해 저장 매체에 있는 데이터에 액세스하기 때문입니다. SessionHandler는 PHP의 내장 SessionHandlerInterface 인터페이스를 균일하게 구현하므로 드라이버는 통합된 인터페이스 방법을 통해 다양한 세션 저장 미디어에 액세스할 수 있습니다. #🎜🎜#

드라이버 액세스 세션 데이터

#🎜🎜# 개발자는 세션 외관 또는 $request->session()을 사용하여 세션에 액세스합니다. data는 모두 session 서비스, 즉 SessionManager 객체를 통해 해당 드라이버 메소드로 전달됩니다. 의 소스 코드에서도 <code>를 볼 수 있습니다. IlluminateSessionStore Laravel에서 사용되는 세션 메소드는 모두 여기에 정의되어 있습니다. #🎜🎜#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;
        });
    }
}

这样在用SessionManagerdriver方法创建mongo类型的驱动器的时候就会调用callCustomCreator方法去创建mongo类型的Session驱动器了。

相关推荐:

如何使用Larave制定一个MySQL数据库备份计划任务

Laravel框架下的配置管理系统的设计过程(附代码)

Laravel中collection类的使用方法总结(代码)

위 내용은 Laravel 프레임워크의 핵심 내용: Session 소스코드 상세 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.