Laravel框架 之 Session

本文的示例代码参考session

目录

  • 总述

  • StartSession

  • EncryptCookies

  • 启动流程与IoC容器

    • 启动流程

    • IoC容器

总述

  • Session基于以下中间件
StartSession # vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php

EncryptCookies # vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php
  • Session的完整流程如下
request -> EncryptCookies (decrypt) -> StartSession (loadSession) -> StartSession (add session to request)

response <- EncryptCookies (encrypt) <- StartSession (add cookie to response) <- StartSession (saveSession)

StartSession

// app/Http/Kernel.php
class Kernel extends HttpKernel
{
    protected $middlewareGroups = [
        'web' => [
            // 设置session中间件
            \Illuminate\Session\Middleware\StartSession::class,
        ],
    ];
}
// vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php
class StartSession
{
    // 处理$request
    public function handle($request, Closure $next)
    {
        // 设置$request session
        if ($this->sessionConfigured()) {
            $request->setLaravelSession(
                $session = $this->startSession($request)
            );
        }

        // $request next中间件
        $response = $next($request);

        // 添加cookie至response
        if ($this->sessionConfigured()) {
            $this->addCookieToResponse($response, $session);
        }

        return $response;
    }

    protected function startSession(Request $request)
    {
        return tap($this->getSession($request), function ($session) use ($request) {
            $session->setRequestOnHandler($request);

            $session->start();
        });
    }

    protected function addCookieToResponse(Response $response, Session $session)
    {
        // 保存持久化session
        if ($this->usingCookieSessions()) {
            $this->manager->driver()->save();
        }

        // 添加cookie至response
        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
            $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
            ));
        }
    }
}
// vendor/laravel/framework/src/Illuminate/Session/Store.php
class Store implements Session
{
    public function start()
    {
        // 读取持久化session
        $this->loadSession();

        return $this->started = true;
    }

    protected function loadSession()
    {
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());
    }

    protected function readFromHandler()
    {
        // 默认session持久化在file中 所以handler实例类型是: vendor/laravel/framework/src/Illuminate/Session/FileSessionHandler.php
        if ($data = $this->handler->read($this->getId())) {
            $data = @unserialize($this->prepareForUnserialize($data));

            if ($data !== false && ! is_null($data) && is_array($data)) {
                return $data;
            }
        }

        return [];
    }
}
cat $(find storage/framework/sessions -name "[0-9a-zA-Z]*" | tail -n1)
# a:3:{s:6:"_token";s:40:"preN7ziem0bBQ3T07b1YnoIvxCNmJJRcvtWl40wo";s:9:"_previous";a:1:{s:3:"url";s:21:"http://localhost:8000";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}

EncryptCookies

// app/Http/Kernel.php
class Kernel extends HttpKernel
{
    protected $middlewareGroups = [
        'web' => [
            // 设置encrypt中间件
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Session\Middleware\StartSession::class,
        ],
    ];
}
// app/Http/Middleware/EncryptCookies.php
class EncryptCookies extends Middleware
{
    protected $except = [
        //
    ];
}
// vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php
class EncryptCookies
{
    public function handle($request, Closure $next)
    {
        // decrypt $request cookie -> 接着交给next中间件处理 -> 最后再encrypt $response cookie
        $response = $next($this->decrypt($request));

        return $this->encrypt($response);
    }

    protected function encrypt(Response $response)
    {
        foreach ($response->headers->getCookies() as $cookie) {
            if ($this->isDisabled($cookie->getName())) {
                continue;
            }

            $response->headers->setCookie($this->duplicate(
                // $this->encrypter实例类型是:vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php
                $cookie, $this->encrypter->encrypt($cookie->getValue())
            ));
        }

        return $response;
    }
}
// vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php
class Encrypter implements EncrypterContract
{
    // 使用对称加密算法加密session id 这里的key即是.env中的APP_KEY
    public function encrypt($value, $serialize = true)
    {
        $iv = random_bytes(openssl_cipher_iv_length($this->cipher));

        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );

        $mac = $this->hash($iv = base64_encode($iv), $value);

        return base64_encode(json_encode(compact('iv', 'value', 'mac')));
    }
}

启动流程与IoC容器

  • 上述解释了Session的流程
那么Session是如何和启动流程相关联的呢?

框架又是如何通过IoC容器管理相关实例的呢?

启动流程

// public/index.php
require __DIR__.'/../vendor/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);
// bootstrap/app.php
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;
// app/Http/Kernel.php
class Kernel extends HttpKernel
{
}
// vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
class Kernel implements KernelContract
{
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];

    public function handle($request)
    {
        $response = $this->sendRequestThroughRouter($request);

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }

    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
}

IoC容器

// vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php
class RegisterProviders
{
    public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }
}
// vendor/laravel/framework/src/Illuminate/Foundation/Application.php
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
    public function registerConfiguredProviders()
    {
        $providers = Collection::make($this->config['app.providers'])
                        ->partition(function ($provider) {
                            return Str::startsWith($provider, 'Illuminate\\');
                        });

        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }
}
// config/app.php
return [
    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],
];

参考

  • Laravel Sessionid 处理机制

  • Laravel Session -- session 的启动与运行源码分析

  • [php]laravel框架容器管理的一些要点

  • Laravel tap 用法

你可能感兴趣的:(Laravel框架 之 Session)