Heim > PHP-Framework > Laravel > Hauptteil

Kerninhalt des Laravel-Frameworks: Detaillierte Analyse des Session-Quellcodes

不言
Freigeben: 2018-08-14 11:16:16
Original
2289 Leute haben es durchsucht

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?

Sitzungsdienst registrieren

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
    ......
],
Nach dem Login kopieren
Tatsächlich gibt es

in providers. Schauen wir uns den Quellcode an und sehen uns die Registrierungsdetails des Sitzungsdienstes an von drei Diensten im SessionServiceProvider

SessionServiceProvider

-Dienst. Nachdem der Sitzungsdienst analysiert wurde, ist er ein
    -Objekt. Seine Funktion besteht darin, den Sitzungstreiber zu erstellen und den Treiber bei Bedarf analysieren (verzögertes Laden). Darüber hinaus werden alle Methodenaufrufe für den Zugriff auf und die Aktualisierung von Sitzungsdaten per Proxy an den entsprechenden Sitzungstreiber implementiert.
  • sessionSessionManager

    Sitzungstreiber, eine Instanz von
  • ,
  • implementiert den

    -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. SessionHandlerdatabaseredismemcache

    Middleware, die die Sitzung zu Beginn der Anfrage öffnet und die Sitzungskennung in das Cookie schreibt, bevor die Antwort zusätzlich als
  • Nachdem die Antwort an den Client gesendet wurde, speichert dieser die Aktualisierungen der Sitzungsdaten in der Anfrage auf dem Speichermedium in der Methode
  • .

    StartSession::classterminate中间件terminate()Sitzungstreiber erstellen

  • Wie oben erwähnt, wird
zum Erstellen eines Sitzungstreibers verwendet, der verschiedene Treiberersteller definiert (Methode „Treiberinstanz erstellen“). Sehen Sie sich die Quelle an Code, um zu sehen, dass der Sitzungstreiber erstellt wird:

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();
        });
    }
}
Nach dem Login kopieren

Durch den Quellcode von

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

die integrierte

-Schnittstelle SessionManager einheitlich implementieren, sodass das Laufwerk über einheitliche Schnittstellenmethoden zugreifen kann Daten in verschiedenen Sitzungsspeichermedien. SessionHandlerSessionHandler Treiber greift auf Sitzungsdaten zu PHPSessionHandlerInterface Entwickler, die die

-Fassade oder

verwenden, um auf Sitzungsdaten zuzugreifen, werden über den

-Dienst, d. h. den -Objekt. Im Quellcode von

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;
    }
}
Nach dem Login kopieren
sessionDie 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);
Nach dem Login kopieren
LaravelDa 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中间件

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;
    }
}
Nach dem Login kopieren

同样的我只保留了最关键的代码,可以看到中间件在请求进来时会先进行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) {}
}
Nach dem Login kopieren

定义完驱动后在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;
        });
    }
}
Nach dem Login kopieren

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

相关推荐:

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

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

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!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage