本文的示例代码参考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 用法