Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Use session identifier instead of memoizing it
Browse files Browse the repository at this point in the history
This patch uses the fact that `Zend\Expressive\Session\Session` now
implements `Zend\Expressive\Session\SessionIdentifierAwareInterface`.
When creating a session, it passes it the session identifier discovered
when introspecting cookies, if any. When persistence is invoked, it
pulls the identifier from the session.

This approach should solve concurrency issues, and makes it more clear
when a session cookie should be sent.
  • Loading branch information
weierophinney committed Sep 12, 2018
1 parent 1693cdc commit 8bf0b3d
Showing 1 changed file with 35 additions and 29 deletions.
64 changes: 35 additions & 29 deletions src/PhpSessionPersistence.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@
*/
class PhpSessionPersistence implements SessionPersistenceInterface
{
/** @var string|null */
private $cookie;

/** @var string */
private $cacheLimiter;

Expand Down Expand Up @@ -91,45 +88,53 @@ public function __construct()
public function initializeSessionFromRequest(ServerRequestInterface $request) : SessionInterface
{
$this->scriptFile = $request->getServerParams()['SCRIPT_FILENAME'] ?? __FILE__;
$this->cookie = FigRequestCookies::get($request, session_name())->getValue();
$id = $this->cookie ?: $this->generateSessionId();
$sessionId = FigRequestCookies::get($request, session_name())->getValue() ?? '';
$id = $sessionId ?: $this->generateSessionId();
$this->startSession($id);
return new Session($_SESSION);
return new Session($_SESSION, $sessionId);
}

public function persistSession(SessionInterface $session, ResponseInterface $response) : ResponseInterface
{
// Regenerate if the session is marked as regenerated
// Regenerate if there is no cookie id set but the session has changed (new session with data)
$id = $session->getId();

// Regenerate if:
// - the session is marked as regenerated
// - the id is empty, but the data has changed (new session)
if ($session->isRegenerated()
|| (! $this->cookie && $session->hasChanged())
|| ('' === $id && $session->hasChanged())
) {
$this->regenerateSession();
$id = $this->regenerateSession();
}

$_SESSION = $session->toArray();
session_write_close();

if ($this->cookie) {
$sessionCookie = SetCookie::create(session_name())
->withValue($this->cookie)
->withPath(ini_get('session.cookie_path'));
// If we do not have an identifier at this point, it means a new
// session was created, but never written to. In that case, there's
// no reason to provide a cookie back to the user.
if ('' === $id) {
return $response;
}

if ($cookieLifetime = (int) ini_get('session.cookie_lifetime')) {
$sessionCookie = $sessionCookie->withExpires(time() + $cookieLifetime);
}
$sessionCookie = SetCookie::create(session_name())
->withValue($id)
->withPath(ini_get('session.cookie_path'));

$response = FigResponseCookies::set($response, $sessionCookie);
if ($cookieLifetime = (int) ini_get('session.cookie_lifetime')) {
$sessionCookie = $sessionCookie->withExpires(time() + $cookieLifetime);
}

if (! $this->cacheLimiter || $this->responseAlreadyHasCacheHeaders($response)) {
return $response;
}
$response = FigResponseCookies::set($response, $sessionCookie);

if (! $this->cacheLimiter || $this->responseAlreadyHasCacheHeaders($response)) {
return $response;
}

$cacheHeaders = $this->generateCacheHeaders();
foreach ($cacheHeaders as $name => $value) {
if (false !== $value) {
$response = $response->withHeader($name, $value);
}
$cacheHeaders = $this->generateCacheHeaders();
foreach ($cacheHeaders as $name => $value) {
if (false !== $value) {
$response = $response->withHeader($name, $value);
}
}

Expand All @@ -154,13 +159,14 @@ private function startSession(string $id, array $options = []) : void
*
* @link http://php.net/manual/en/function.session-regenerate-id.php (Example #2)
*/
private function regenerateSession() : void
private function regenerateSession() : string
{
session_write_close();
$this->cookie = $this->generateSessionId();
$this->startSession($this->cookie, [
$id = $this->generateSessionId();
$this->startSession($id, [
'use_strict_mode' => false,
]);
return $id;
}

/**
Expand Down

0 comments on commit 8bf0b3d

Please sign in to comment.