Upgrade framework

This commit is contained in:
2023-11-14 16:54:35 +01:00
parent 1648a5cd42
commit 4fcf6fffcc
10548 changed files with 693138 additions and 466698 deletions

View File

@@ -0,0 +1,139 @@
<?php
namespace Illuminate\Session;
use Illuminate\Support\InteractsWithTime;
use SessionHandlerInterface;
class ArraySessionHandler implements SessionHandlerInterface
{
use InteractsWithTime;
/**
* The array of stored values.
*
* @var array
*/
protected $storage = [];
/**
* The number of minutes the session should be valid.
*
* @var int
*/
protected $minutes;
/**
* Create a new array driven handler instance.
*
* @param int $minutes
* @return void
*/
public function __construct($minutes)
{
$this->minutes = $minutes;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string|false
*/
public function read($sessionId): string|false
{
if (! isset($this->storage[$sessionId])) {
return '';
}
$session = $this->storage[$sessionId];
$expiration = $this->calculateExpiration($this->minutes * 60);
if (isset($session['time']) && $session['time'] >= $expiration) {
return $session['data'];
}
return '';
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data): bool
{
$this->storage[$sessionId] = [
'data' => $data,
'time' => $this->currentTime(),
];
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId): bool
{
if (isset($this->storage[$sessionId])) {
unset($this->storage[$sessionId]);
}
return true;
}
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime): int
{
$expiration = $this->calculateExpiration($lifetime);
$deletedSessions = 0;
foreach ($this->storage as $sessionId => $session) {
if ($session['time'] < $expiration) {
unset($this->storage[$sessionId]);
$deletedSessions++;
}
}
return $deletedSessions;
}
/**
* Get the expiration time of the session.
*
* @param int $seconds
* @return int
*/
protected function calculateExpiration($seconds)
{
return $this->currentTime() - $seconds;
}
}

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Session;
use SessionHandlerInterface;
use Illuminate\Contracts\Cache\Repository as CacheContract;
use SessionHandlerInterface;
class CacheBasedSessionHandler implements SessionHandlerInterface
{
@@ -36,50 +36,62 @@ class CacheBasedSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName)
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close()
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string
*/
public function read($sessionId)
public function read($sessionId): string
{
return $this->cache->get($sessionId, '');
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data)
public function write($sessionId, $data): bool
{
return $this->cache->put($sessionId, $data, $this->minutes);
return $this->cache->put($sessionId, $data, $this->minutes * 60);
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId)
public function destroy($sessionId): bool
{
return $this->cache->forget($sessionId);
}
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime)
public function gc($lifetime): int
{
return true;
return 0;
}
/**

View File

@@ -3,9 +3,11 @@
namespace Illuminate\Session\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Composer;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Composer;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'session:table')]
class SessionTableCommand extends Command
{
/**
@@ -15,6 +17,17 @@ class SessionTableCommand extends Command
*/
protected $name = 'session:table';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'session:table';
/**
* The console command description.
*
@@ -54,13 +67,13 @@ class SessionTableCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$fullPath = $this->createBaseMigration();
$this->files->put($fullPath, $this->files->get(__DIR__.'/stubs/database.stub'));
$this->info('Migration created successfully!');
$this->components->info('Migration created successfully.');
$this->composer->dumpAutoloads();
}

View File

@@ -1,10 +1,10 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSessionsTable extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@@ -14,12 +14,12 @@ class CreateSessionsTable extends Migration
public function up()
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
@@ -32,4 +32,4 @@ class CreateSessionsTable extends Migration
{
Schema::dropIfExists('sessions');
}
}
};

View File

@@ -2,13 +2,15 @@
namespace Illuminate\Session;
use Carbon\Carbon;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
use Illuminate\Support\InteractsWithTime;
use SessionHandlerInterface;
use Symfony\Component\HttpFoundation\Request;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
class CookieSessionHandler implements SessionHandlerInterface
{
use InteractsWithTime;
/**
* The cookie jar instance.
*
@@ -45,31 +47,36 @@ class CookieSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName)
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close()
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string|false
*/
public function read($sessionId)
public function read($sessionId): string|false
{
$value = $this->request->cookies->get($sessionId) ?: '';
if (! is_null($decoded = json_decode($value, true)) && is_array($decoded)) {
if (isset($decoded['expires']) && Carbon::now()->getTimestamp() <= $decoded['expires']) {
return $decoded['data'];
}
if (! is_null($decoded = json_decode($value, true)) && is_array($decoded) &&
isset($decoded['expires']) && $this->currentTime() <= $decoded['expires']) {
return $decoded['data'];
}
return '';
@@ -77,12 +84,14 @@ class CookieSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data)
public function write($sessionId, $data): bool
{
$this->cookie->queue($sessionId, json_encode([
'data' => $data,
'expires' => Carbon::now()->addMinutes($this->minutes)->getTimestamp(),
'expires' => $this->availableAt($this->minutes * 60),
]), $this->minutes);
return true;
@@ -90,8 +99,10 @@ class CookieSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId)
public function destroy($sessionId): bool
{
$this->cookie->queue($this->cookie->forget($sessionId));
@@ -100,10 +111,12 @@ class CookieSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime)
public function gc($lifetime): int
{
return true;
return 0;
}
/**

View File

@@ -2,16 +2,19 @@
namespace Illuminate\Session;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use SessionHandlerInterface;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\QueryException;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\QueryException;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\InteractsWithTime;
use SessionHandlerInterface;
class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareInterface
class DatabaseSessionHandler implements ExistenceAwareInterface, SessionHandlerInterface
{
use InteractsWithTime;
/**
* The database connection instance.
*
@@ -66,31 +69,37 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName)
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close()
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string|false
*/
public function read($sessionId)
public function read($sessionId): string|false
{
$session = (object) $this->getQuery()->find($sessionId);
if ($this->expired($session)) {
$this->exists = true;
return;
return '';
}
if (isset($session->payload)) {
@@ -98,6 +107,8 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
return base64_decode($session->payload);
}
return '';
}
/**
@@ -114,8 +125,10 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data)
public function write($sessionId, $data): bool
{
$payload = $this->getDefaultPayload($data);
@@ -136,8 +149,8 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
* Perform an insert operation on the session ID.
*
* @param string $sessionId
* @param string $payload
* @return void
* @param array<string, mixed> $payload
* @return bool|null
*/
protected function performInsert($sessionId, $payload)
{
@@ -152,7 +165,7 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
* Perform an update operation on the session ID.
*
* @param string $sessionId
* @param string $payload
* @param array<string, mixed> $payload
* @return int
*/
protected function performUpdate($sessionId, $payload)
@@ -170,7 +183,7 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
{
$payload = [
'payload' => base64_encode($data),
'last_activity' => Carbon::now()->getTimestamp(),
'last_activity' => $this->currentTime(),
];
if (! $this->container) {
@@ -248,8 +261,10 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId)
public function destroy($sessionId): bool
{
$this->getQuery()->where('id', $sessionId)->delete();
@@ -258,10 +273,12 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime)
public function gc($lifetime): int
{
$this->getQuery()->where('last_activity', '<=', Carbon::now()->getTimestamp() - $lifetime)->delete();
return $this->getQuery()->where('last_activity', '<=', $this->currentTime() - $lifetime)->delete();
}
/**
@@ -274,6 +291,19 @@ class DatabaseSessionHandler implements SessionHandlerInterface, ExistenceAwareI
return $this->connection->table($this->table);
}
/**
* Set the application instance used by the handler.
*
* @param \Illuminate\Contracts\Foundation\Application $container
* @return $this
*/
public function setContainer($container)
{
$this->container = $container;
return $this;
}
/**
* Set the existence state for the session.
*

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Session;
use SessionHandlerInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;
use SessionHandlerInterface;
class EncryptedStore extends Store
{
@@ -18,17 +18,18 @@ class EncryptedStore extends Store
/**
* Create a new session instance.
*
* @param string $name
* @param \SessionHandlerInterface $handler
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param string|null $id
* @param string $name
* @param \SessionHandlerInterface $handler
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param string|null $id
* @param string $serialization
* @return void
*/
public function __construct($name, SessionHandlerInterface $handler, EncrypterContract $encrypter, $id = null)
public function __construct($name, SessionHandlerInterface $handler, EncrypterContract $encrypter, $id = null, $serialization = 'php')
{
$this->encrypter = $encrypter;
parent::__construct($name, $handler, $id);
parent::__construct($name, $handler, $id, $serialization);
}
/**
@@ -42,7 +43,7 @@ class EncryptedStore extends Store
try {
return $this->encrypter->decrypt($data);
} catch (DecryptException $e) {
return serialize([]);
return $this->serialization === 'json' ? json_encode([]) : serialize([]);
}
}

View File

@@ -2,10 +2,10 @@
namespace Illuminate\Session;
use Carbon\Carbon;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Carbon;
use SessionHandlerInterface;
use Symfony\Component\Finder\Finder;
use Illuminate\Filesystem\Filesystem;
class FileSessionHandler implements SessionHandlerInterface
{
@@ -47,29 +47,34 @@ class FileSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName)
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close()
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string|false
*/
public function read($sessionId)
public function read($sessionId): string|false
{
if ($this->files->exists($path = $this->path.'/'.$sessionId)) {
if (filemtime($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) {
return $this->files->get($path, true);
}
if ($this->files->isFile($path = $this->path.'/'.$sessionId) &&
$this->files->lastModified($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) {
return $this->files->sharedGet($path);
}
return '';
@@ -77,8 +82,10 @@ class FileSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data)
public function write($sessionId, $data): bool
{
$this->files->put($this->path.'/'.$sessionId, $data, true);
@@ -87,8 +94,10 @@ class FileSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId)
public function destroy($sessionId): bool
{
$this->files->delete($this->path.'/'.$sessionId);
@@ -97,8 +106,10 @@ class FileSessionHandler implements SessionHandlerInterface
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime)
public function gc($lifetime): int
{
$files = Finder::create()
->in($this->path)
@@ -106,8 +117,13 @@ class FileSessionHandler implements SessionHandlerInterface
->ignoreDotFiles(true)
->date('<= now - '.$lifetime.' seconds');
$deletedSessions = 0;
foreach ($files as $file) {
$this->files->delete($file->getRealPath());
$deletedSessions++;
}
return $deletedSessions;
}
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -5,8 +5,9 @@ namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Contracts\Session\Middleware\AuthenticatesSessions;
class AuthenticateSession
class AuthenticateSession implements AuthenticatesSessions
{
/**
* The authentication factory implementation.
@@ -35,24 +36,30 @@ class AuthenticateSession
*/
public function handle($request, Closure $next)
{
if (! $request->user() || ! $request->session()) {
if (! $request->hasSession() || ! $request->user()) {
return $next($request);
}
if (! $request->session()->has('password_hash') && $this->auth->viaRemember()) {
$this->logout($request);
if ($this->guard()->viaRemember()) {
$passwordHash = explode('|', $request->cookies->get($this->guard()->getRecallerName()))[2] ?? null;
if (! $passwordHash || $passwordHash != $request->user()->getAuthPassword()) {
$this->logout($request);
}
}
if (! $request->session()->has('password_hash')) {
if (! $request->session()->has('password_hash_'.$this->auth->getDefaultDriver())) {
$this->storePasswordHashInSession($request);
}
if ($request->session()->get('password_hash') !== $request->user()->getAuthPassword()) {
if ($request->session()->get('password_hash_'.$this->auth->getDefaultDriver()) !== $request->user()->getAuthPassword()) {
$this->logout($request);
}
return tap($next($request), function () use ($request) {
$this->storePasswordHashInSession($request);
if (! is_null($this->guard()->user())) {
$this->storePasswordHashInSession($request);
}
});
}
@@ -69,7 +76,7 @@ class AuthenticateSession
}
$request->session()->put([
'password_hash' => $request->user()->getAuthPassword(),
'password_hash_'.$this->auth->getDefaultDriver() => $request->user()->getAuthPassword(),
]);
}
@@ -83,10 +90,20 @@ class AuthenticateSession
*/
protected function logout($request)
{
$this->auth->logout();
$this->guard()->logoutCurrentDevice();
$request->session()->flush();
throw new AuthenticationException;
throw new AuthenticationException('Unauthenticated.', [$this->auth->getDefaultDriver()]);
}
/**
* Get the guard instance that should be used by the middleware.
*
* @return \Illuminate\Contracts\Auth\Factory|\Illuminate\Contracts\Auth\Guard
*/
protected function guard()
{
return $this->auth;
}
}

View File

@@ -3,12 +3,12 @@
namespace Illuminate\Session\Middleware;
use Closure;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Session\Session;
use Illuminate\Session\CookieSessionHandler;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Session\SessionManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Date;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;
@@ -22,21 +22,23 @@ class StartSession
protected $manager;
/**
* Indicates if the session was handled for the current request.
* The callback that can resolve an instance of the cache factory.
*
* @var bool
* @var callable|null
*/
protected $sessionHandled = false;
protected $cacheFactoryResolver;
/**
* Create a new session middleware.
*
* @param \Illuminate\Session\SessionManager $manager
* @param callable|null $cacheFactoryResolver
* @return void
*/
public function __construct(SessionManager $manager)
public function __construct(SessionManager $manager, callable $cacheFactoryResolver = null)
{
$this->manager = $manager;
$this->cacheFactoryResolver = $cacheFactoryResolver;
}
/**
@@ -48,56 +50,98 @@ class StartSession
*/
public function handle($request, Closure $next)
{
$this->sessionHandled = true;
if (! $this->sessionConfigured()) {
return $next($request);
}
$session = $this->getSession($request);
if ($this->manager->shouldBlock() ||
($request->route() instanceof Route && $request->route()->locksFor())) {
return $this->handleRequestWhileBlocking($request, $session, $next);
}
return $this->handleStatefulRequest($request, $session, $next);
}
/**
* Handle the given request within session state.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Contracts\Session\Session $session
* @param \Closure $next
* @return mixed
*/
protected function handleRequestWhileBlocking(Request $request, $session, Closure $next)
{
if (! $request->route() instanceof Route) {
return;
}
$lockFor = $request->route() && $request->route()->locksFor()
? $request->route()->locksFor()
: 10;
$lock = $this->cache($this->manager->blockDriver())
->lock('session:'.$session->getId(), $lockFor)
->betweenBlockedAttemptsSleepFor(50);
try {
$lock->block(
! is_null($request->route()->waitsFor())
? $request->route()->waitsFor()
: 10
);
return $this->handleStatefulRequest($request, $session, $next);
} finally {
$lock?->release();
}
}
/**
* Handle the given request within session state.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Contracts\Session\Session $session
* @param \Closure $next
* @return mixed
*/
protected function handleStatefulRequest(Request $request, $session, Closure $next)
{
// 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)
);
$request->setLaravelSession(
$this->startSession($request, $session)
);
$this->collectGarbage($session);
}
$this->collectGarbage($session);
$response = $next($request);
$this->storeCurrentUrl($request, $session);
$this->addCookieToResponse($response, $session);
// 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);
}
$this->saveSession($request);
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
* @param \Illuminate\Contracts\Session\Session $session
* @return \Illuminate\Contracts\Session\Session
*/
protected function startSession(Request $request)
protected function startSession(Request $request, $session)
{
return tap($this->getSession($request), function ($session) use ($request) {
return tap($session, function ($session) use ($request) {
$session->setRequestOnHandler($request);
$session->start();
@@ -155,7 +199,11 @@ class StartSession
*/
protected function storeCurrentUrl(Request $request, $session)
{
if ($request->method() === 'GET' && $request->route() && ! $request->ajax()) {
if ($request->isMethod('GET') &&
$request->route() instanceof Route &&
! $request->ajax() &&
! $request->prefetch() &&
! $request->isPrecognitive()) {
$session->setPreviousUrl($request->fullUrl());
}
}
@@ -169,19 +217,28 @@ class StartSession
*/
protected function addCookieToResponse(Response $response, Session $session)
{
if ($this->usingCookieSessions()) {
$this->manager->driver()->save();
}
if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
$response->headers->setCookie(new Cookie(
$session->getName(), $session->getId(), $this->getCookieExpirationDate(),
$config['path'], $config['domain'], Arr::get($config, 'secure', false),
Arr::get($config, 'http_only', true)
$config['path'], $config['domain'], $config['secure'] ?? false,
$config['http_only'] ?? true, false, $config['same_site'] ?? null
));
}
}
/**
* Save the session data to storage.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function saveSession($request)
{
if (! $request->isPrecognitive()) {
$this->manager->driver()->save();
}
}
/**
* Get the session lifetime in seconds.
*
@@ -189,19 +246,21 @@ class StartSession
*/
protected function getSessionLifetimeInSeconds()
{
return Arr::get($this->manager->getSessionConfig(), 'lifetime') * 60;
return ($this->manager->getSessionConfig()['lifetime'] ?? null) * 60;
}
/**
* Get the cookie lifetime in seconds.
*
* @return \DateTimeInterface
* @return \DateTimeInterface|int
*/
protected function getCookieExpirationDate()
{
$config = $this->manager->getSessionConfig();
return $config['expire_on_close'] ? 0 : Carbon::now()->addMinutes($config['lifetime']);
return $config['expire_on_close'] ? 0 : Date::instance(
Carbon::now()->addRealMinutes($config['lifetime'])
);
}
/**
@@ -211,7 +270,7 @@ class StartSession
*/
protected function sessionConfigured()
{
return ! is_null(Arr::get($this->manager->getSessionConfig(), 'driver'));
return ! is_null($this->manager->getSessionConfig()['driver'] ?? null);
}
/**
@@ -224,20 +283,17 @@ class StartSession
{
$config = $config ?: $this->manager->getSessionConfig();
return ! in_array($config['driver'], [null, 'array']);
return ! is_null($config['driver'] ?? null);
}
/**
* Determine if the session is using cookie sessions.
* Resolve the given cache driver.
*
* @return bool
* @param string $driver
* @return \Illuminate\Cache\Store
*/
protected function usingCookieSessions()
protected function cache($driver)
{
if ($this->sessionConfigured()) {
return $this->manager->driver()->getHandler() instanceof CookieSessionHandler;
}
return false;
return call_user_func($this->cacheFactoryResolver)->driver($driver);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Illuminate\Session;
use SessionHandlerInterface;
class NullSessionHandler implements SessionHandlerInterface
{
/**
* {@inheritdoc}
*
* @return bool
*/
public function open($savePath, $sessionName): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function close(): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return string
*/
public function read($sessionId): string
{
return '';
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function write($sessionId, $data): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function destroy($sessionId): bool
{
return true;
}
/**
* {@inheritdoc}
*
* @return int
*/
public function gc($lifetime): int
{
return 0;
}
}

View File

@@ -3,8 +3,10 @@
namespace Illuminate\Session;
use Illuminate\Support\Manager;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
/**
* @mixin \Illuminate\Session\Store
*/
class SessionManager extends Manager
{
/**
@@ -18,6 +20,16 @@ class SessionManager extends Manager
return $this->buildSession(parent::callCustomCreator($driver));
}
/**
* Create an instance of the "null" session driver.
*
* @return \Illuminate\Session\Store
*/
protected function createNullDriver()
{
return $this->buildSession(new NullSessionHandler);
}
/**
* Create an instance of the "array" session driver.
*
@@ -25,7 +37,9 @@ class SessionManager extends Manager
*/
protected function createArrayDriver()
{
return $this->buildSession(new NullSessionHandler);
return $this->buildSession(new ArraySessionHandler(
$this->config->get('session.lifetime')
));
}
/**
@@ -36,7 +50,7 @@ class SessionManager extends Manager
protected function createCookieDriver()
{
return $this->buildSession(new CookieSessionHandler(
$this->app['cookie'], $this->app['config']['session.lifetime']
$this->container->make('cookie'), $this->config->get('session.lifetime')
));
}
@@ -57,10 +71,10 @@ class SessionManager extends Manager
*/
protected function createNativeDriver()
{
$lifetime = $this->app['config']['session.lifetime'];
$lifetime = $this->config->get('session.lifetime');
return $this->buildSession(new FileSessionHandler(
$this->app['files'], $this->app['config']['session.files'], $lifetime
$this->container->make('files'), $this->config->get('session.files'), $lifetime
));
}
@@ -71,12 +85,12 @@ class SessionManager extends Manager
*/
protected function createDatabaseDriver()
{
$table = $this->app['config']['session.table'];
$table = $this->config->get('session.table');
$lifetime = $this->app['config']['session.lifetime'];
$lifetime = $this->config->get('session.lifetime');
return $this->buildSession(new DatabaseSessionHandler(
$this->getDatabaseConnection(), $table, $lifetime, $this->app
$this->getDatabaseConnection(), $table, $lifetime, $this->container
));
}
@@ -87,9 +101,9 @@ class SessionManager extends Manager
*/
protected function getDatabaseConnection()
{
$connection = $this->app['config']['session.connection'];
$connection = $this->config->get('session.connection');
return $this->app['db']->connection($connection);
return $this->container->make('db')->connection($connection);
}
/**
@@ -122,12 +136,22 @@ class SessionManager extends Manager
$handler = $this->createCacheHandler('redis');
$handler->getCache()->getStore()->setConnection(
$this->app['config']['session.connection']
$this->config->get('session.connection')
);
return $this->buildSession($handler);
}
/**
* Create an instance of the DynamoDB session driver.
*
* @return \Illuminate\Session\Store
*/
protected function createDynamodbDriver()
{
return $this->createCacheBased('dynamodb');
}
/**
* Create an instance of a cache driven driver.
*
@@ -147,11 +171,11 @@ class SessionManager extends Manager
*/
protected function createCacheHandler($driver)
{
$store = $this->app['config']->get('session.store') ?: $driver;
$store = $this->config->get('session.store') ?: $driver;
return new CacheBasedSessionHandler(
clone $this->app['cache']->store($store),
$this->app['config']['session.lifetime']
clone $this->container->make('cache')->store($store),
$this->config->get('session.lifetime')
);
}
@@ -163,11 +187,14 @@ class SessionManager extends Manager
*/
protected function buildSession($handler)
{
if ($this->app['config']['session.encrypt']) {
return $this->buildEncryptedSession($handler);
} else {
return new Store($this->app['config']['session.cookie'], $handler);
}
return $this->config->get('session.encrypt')
? $this->buildEncryptedSession($handler)
: new Store(
$this->config->get('session.cookie'),
$handler,
$id = null,
$this->config->get('session.serialization', 'php')
);
}
/**
@@ -179,10 +206,34 @@ class SessionManager extends Manager
protected function buildEncryptedSession($handler)
{
return new EncryptedStore(
$this->app['config']['session.cookie'], $handler, $this->app['encrypter']
$this->config->get('session.cookie'),
$handler,
$this->container['encrypter'],
$id = null,
$this->config->get('session.serialization', 'php'),
);
}
/**
* Determine if requests for the same session should wait for each to finish before executing.
*
* @return bool
*/
public function shouldBlock()
{
return $this->config->get('session.block', false);
}
/**
* Get the name of the cache store / driver that should be used to acquire session locks.
*
* @return string|null
*/
public function blockDriver()
{
return $this->config->get('session.block_store');
}
/**
* Get the session configuration.
*
@@ -190,7 +241,7 @@ class SessionManager extends Manager
*/
public function getSessionConfig()
{
return $this->app['config']['session'];
return $this->config->get('session');
}
/**
@@ -200,7 +251,7 @@ class SessionManager extends Manager
*/
public function getDefaultDriver()
{
return $this->app['config']['session.driver'];
return $this->config->get('session.driver');
}
/**
@@ -211,6 +262,6 @@ class SessionManager extends Manager
*/
public function setDefaultDriver($name)
{
$this->app['config']['session.driver'] = $name;
$this->config->set('session.driver', $name);
}
}

View File

@@ -2,8 +2,9 @@
namespace Illuminate\Session;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Cache\Factory as CacheFactory;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
@@ -18,7 +19,11 @@ class SessionServiceProvider extends ServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartSession::class);
$this->app->singleton(StartSession::class, function ($app) {
return new StartSession($app->make(SessionManager::class), function () use ($app) {
return $app->make(CacheFactory::class);
});
});
}
/**

View File

@@ -3,13 +3,19 @@
namespace Illuminate\Session;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use SessionHandlerInterface;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Arr;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Support\ViewErrorBag;
use SessionHandlerInterface;
use stdClass;
class Store implements Session
{
use Macroable;
/**
* The session ID.
*
@@ -38,6 +44,13 @@ class Store implements Session
*/
protected $handler;
/**
* The session store's serialization strategy.
*
* @var string
*/
protected $serialization = 'php';
/**
* Session store started status.
*
@@ -48,16 +61,18 @@ class Store implements Session
/**
* Create a new session instance.
*
* @param string $name
* @param \SessionHandlerInterface $handler
* @param string|null $id
* @param string $name
* @param \SessionHandlerInterface $handler
* @param string|null $id
* @param string $serialization
* @return void
*/
public function __construct($name, SessionHandlerInterface $handler, $id = null)
public function __construct($name, SessionHandlerInterface $handler, $id = null, $serialization = 'php')
{
$this->setId($id);
$this->name = $name;
$this->handler = $handler;
$this->serialization = $serialization;
}
/**
@@ -84,6 +99,8 @@ class Store implements Session
protected function loadSession()
{
$this->attributes = array_merge($this->attributes, $this->readFromHandler());
$this->marshalErrorBag();
}
/**
@@ -94,9 +111,13 @@ class Store implements Session
protected function readFromHandler()
{
if ($data = $this->handler->read($this->getId())) {
$data = @unserialize($this->prepareForUnserialize($data));
if ($this->serialization === 'json') {
$data = json_decode($this->prepareForUnserialize($data), true);
} else {
$data = @unserialize($this->prepareForUnserialize($data));
}
if ($data !== false && ! is_null($data) && is_array($data)) {
if ($data !== false && is_array($data)) {
return $data;
}
}
@@ -115,22 +136,69 @@ class Store implements Session
return $data;
}
/**
* Marshal the ViewErrorBag when using JSON serialization for sessions.
*
* @return void
*/
protected function marshalErrorBag()
{
if ($this->serialization !== 'json' || $this->missing('errors')) {
return;
}
$errorBag = new ViewErrorBag;
foreach ($this->get('errors') as $key => $value) {
$messageBag = new MessageBag($value['messages']);
$errorBag->put($key, $messageBag->setFormat($value['format']));
}
$this->put('errors', $errorBag);
}
/**
* Save the session data to storage.
*
* @return bool
* @return void
*/
public function save()
{
$this->ageFlashData();
$this->prepareErrorBagForSerialization();
$this->handler->write($this->getId(), $this->prepareForStorage(
serialize($this->attributes)
$this->serialization === 'json' ? json_encode($this->attributes) : serialize($this->attributes)
));
$this->started = false;
}
/**
* Prepare the ViewErrorBag instance for JSON serialization.
*
* @return void
*/
protected function prepareErrorBagForSerialization()
{
if ($this->serialization !== 'json' || $this->missing('errors')) {
return;
}
$errors = [];
foreach ($this->attributes['errors']->getBags() as $key => $value) {
$errors[$key] = [
'format' => $value->getFormat(),
'messages' => $value->getMessages(),
];
}
$this->attributes['errors'] = $errors;
}
/**
* Prepare the serialized session data for storage.
*
@@ -166,6 +234,17 @@ class Store implements Session
return $this->attributes;
}
/**
* Get a subset of the session data.
*
* @param array $keys
* @return array
*/
public function only(array $keys)
{
return Arr::only($this->attributes, $keys);
}
/**
* Checks if a key exists.
*
@@ -174,11 +253,24 @@ class Store implements Session
*/
public function exists($key)
{
return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) {
return ! Arr::exists($this->attributes, $key);
$placeholder = new stdClass;
return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) use ($placeholder) {
return $this->get($key, $placeholder) === $placeholder;
});
}
/**
* Determine if the given key is missing from the session data.
*
* @param string|array $key
* @return bool
*/
public function missing($key)
{
return ! $this->exists($key);
}
/**
* Checks if a key is present and not null.
*
@@ -208,7 +300,7 @@ class Store implements Session
* Get the value of a given key and then forget it.
*
* @param string $key
* @param string $default
* @param mixed $default
* @return mixed
*/
public function pull($key, $default = null)
@@ -219,7 +311,7 @@ class Store implements Session
/**
* Determine if the session contains old input.
*
* @param string $key
* @param string|null $key
* @return bool
*/
public function hasOldInput($key = null)
@@ -232,8 +324,8 @@ class Store implements Session
/**
* Get the requested item from the flashed input array.
*
* @param string $key
* @param mixed $default
* @param string|null $key
* @param mixed $default
* @return mixed
*/
public function getOldInput($key = null, $default = null)
@@ -256,7 +348,7 @@ class Store implements Session
* Put a key / value pair or array of key / value pairs in the session.
*
* @param string|array $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function put($key, $value = null)
@@ -292,7 +384,7 @@ class Store implements Session
* Push a value onto a session array.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function push($key, $value)
@@ -334,10 +426,10 @@ class Store implements Session
* Flash a key / value pair to the session.
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return void
*/
public function flash($key, $value)
public function flash(string $key, $value = true)
{
$this->put($key, $value);
@@ -349,8 +441,8 @@ class Store implements Session
/**
* Flash a key / value pair to the session for immediate use.
*
* @param string $key
* @param mixed $value
* @param string $key
* @param mixed $value
* @return void
*/
public function now($key, $value)
@@ -472,7 +564,9 @@ class Store implements Session
*/
public function regenerate($destroy = false)
{
return $this->migrate($destroy);
return tap($this->migrate($destroy), function () {
$this->regenerateToken();
});
}
/**
@@ -621,6 +715,16 @@ class Store implements Session
$this->put('_previous.url', $url);
}
/**
* Specify that the user has confirmed their password.
*
* @return void
*/
public function passwordConfirmed()
{
$this->put('auth.password_confirmed_at', time());
}
/**
* Get the underlying session handler implementation.
*
@@ -631,6 +735,17 @@ class Store implements Session
return $this->handler;
}
/**
* Set the underlying session handler implementation.
*
* @param \SessionHandlerInterface $handler
* @return void
*/
public function setHandler(SessionHandlerInterface $handler)
{
return $this->handler = $handler;
}
/**
* Determine if the session handler needs a request.
*

View File

@@ -0,0 +1,186 @@
<?php
namespace Illuminate\Session;
use BadMethodCallException;
use Illuminate\Contracts\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
class SymfonySessionDecorator implements SessionInterface
{
/**
* The underlying Laravel session store.
*
* @var \Illuminate\Session\Store
*/
protected $store;
/**
* Create a new session decorator.
*
* @param \Illuminate\Contracts\Session\Session $store
* @return void
*/
public function __construct(Session $store)
{
$this->store = $store;
}
/**
* {@inheritdoc}
*/
public function start(): bool
{
return $this->store->start();
}
/**
* {@inheritdoc}
*/
public function getId(): string
{
return $this->store->getId();
}
/**
* {@inheritdoc}
*/
public function setId(string $id)
{
$this->store->setId($id);
}
/**
* {@inheritdoc}
*/
public function getName(): string
{
return $this->store->getName();
}
/**
* {@inheritdoc}
*/
public function setName(string $name)
{
$this->store->setName($name);
}
/**
* {@inheritdoc}
*/
public function invalidate(int $lifetime = null): bool
{
$this->store->invalidate();
return true;
}
/**
* {@inheritdoc}
*/
public function migrate(bool $destroy = false, int $lifetime = null): bool
{
$this->store->migrate($destroy);
return true;
}
/**
* {@inheritdoc}
*/
public function save()
{
$this->store->save();
}
/**
* {@inheritdoc}
*/
public function has(string $name): bool
{
return $this->store->has($name);
}
/**
* {@inheritdoc}
*/
public function get(string $name, mixed $default = null): mixed
{
return $this->store->get($name, $default);
}
/**
* {@inheritdoc}
*/
public function set(string $name, mixed $value)
{
$this->store->put($name, $value);
}
/**
* {@inheritdoc}
*/
public function all(): array
{
return $this->store->all();
}
/**
* {@inheritdoc}
*/
public function replace(array $attributes)
{
$this->store->replace($attributes);
}
/**
* {@inheritdoc}
*/
public function remove(string $name): mixed
{
return $this->store->remove($name);
}
/**
* {@inheritdoc}
*/
public function clear()
{
$this->store->flush();
}
/**
* {@inheritdoc}
*/
public function isStarted(): bool
{
return $this->store->isStarted();
}
/**
* {@inheritdoc}
*/
public function registerBag(SessionBagInterface $bag)
{
throw new BadMethodCallException('Method not implemented by Laravel.');
}
/**
* {@inheritdoc}
*/
public function getBag(string $name): SessionBagInterface
{
throw new BadMethodCallException('Method not implemented by Laravel.');
}
/**
* {@inheritdoc}
*/
public function getMetadataBag(): MetadataBag
{
throw new BadMethodCallException('Method not implemented by Laravel.');
}
}

View File

@@ -14,13 +14,14 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/contracts": "5.4.*",
"illuminate/filesystem": "5.4.*",
"illuminate/support": "5.4.*",
"nesbot/carbon": "~1.20",
"symfony/finder": "~3.2",
"symfony/http-foundation": "~3.2"
"php": "^8.0.2",
"ext-json": "*",
"illuminate/collections": "^9.0",
"illuminate/contracts": "^9.0",
"illuminate/filesystem": "^9.0",
"illuminate/support": "^9.0",
"symfony/finder": "^6.0",
"symfony/http-foundation": "^6.0"
},
"autoload": {
"psr-4": {
@@ -29,11 +30,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "9.x-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the session:table command (5.4.*)."
"illuminate/console": "Required to use the session:table command (^9.0)."
},
"config": {
"sort-packages": true