Skip to content

Instantly share code, notes, and snippets.

@azjezz
Last active October 7, 2018 23:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save azjezz/8a32d87df39422a12e3078a35a8c988a to your computer and use it in GitHub Desktop.
Save azjezz/8a32d87df39422a12e3078a35a8c988a to your computer and use it in GitHub Desktop.
PSR-16 based session persistence for zend expressive
<?php
/*──────────────────────────────────────────────────────────────────────────────
─ @author Saif Eddin Gmati <azjezz2@gmail.com>
─────────────────────────────────────────────────────────────────────────────*/
declare(strict_types=1);
namespace Polar\Session;
use DateTime;
use Dflydev\FigCookies\FigResponseCookies;
use Dflydev\FigCookies\Modifier\SameSite;
use Dflydev\FigCookies\SetCookie;
use Exception;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Expressive\Session\Session;
use Zend\Expressive\Session\SessionInterface;
use Zend\Expressive\Session\SessionPersistenceInterface;
use function bin2hex;
use function random_bytes;
class SessionPersistence implements SessionPersistenceInterface
{
public const DEFAULT_COOKIE_OPTIONS = [
'name' => 'session',
'secure' => false,
'expires' => '+30 days',
'path' => '/',
'http_only' => true,
];
/** @var CacheItemPoolInterface $itemPool */
protected $itemPool;
/**
* @var array
*/
protected $cookieOptions;
/**
* Session constructor.
*
* @param CacheItemPoolInterface $itemPool
* @param array $cookieOptions
*/
public function __construct(CacheItemPoolInterface $itemPool, array $cookieOptions = [])
{
$this->itemPool = $itemPool;
$this->cookieOptions = array_merge($cookieOptions, static::DEFAULT_COOKIE_OPTIONS);
}
/**
* Generate a session data instance based on the request.
*
* @param ServerRequestInterface $request
*
* @return SessionInterface
*
* @throws Exception
* @throws InvalidArgumentException
*/
public function initializeSessionFromRequest(ServerRequestInterface $request): SessionInterface
{
// for some reason, $request is missing 'Cookie' header, but i was able to get the cookies via getCookieParams();
// $id = FigRequestCookies::get($request,'session')->getValue() ?? $this->generateSessionId();
$cookies = $request->getCookieParams();
$id = $cookies['session'] ?? $this->generateSessionId();
$data = $this->itemPool->getItem("session.{$id}");
$data = $data->isHit() ? $data->get() : [];
return new Session($data, $id);
}
/**
* Persist the session data instance.
*
* Persists the session data, returning a response instance with any
* artifacts required to return to the client.
*
* @param SessionInterface $session
* @param ResponseInterface $response
*
* @return ResponseInterface
*
* @throws InvalidArgumentException
* @throws Exception
*/
public function persistSession(SessionInterface $session, ResponseInterface $response): ResponseInterface
{
/** @var Session $session */
$id = $session->isRegenerated() ? $this->generateSessionId() : $session->getId();
$this->itemPool->save(
$this->itemPool->getItem("session.{$id}")
->set($session->toArray())
->expiresAfter(360000)
);
$cookieOptions = $this->getCookieOptions();
$sessionCookie = SetCookie::create($cookieOptions['name'])
->withValue($id)
->withSecure($cookieOptions['secure'])
->withExpires(new DateTime($cookieOptions['expires']))
->withPath($cookieOptions['path'])
->withHttpOnly($cookieOptions['http_only']);
if (isset($cookieOptions['same_site'])) {
$sessionCookie = $sessionCookie->withSameSite(SameSite::fromString($cookieOptions['same_site']));
}
return FigResponseCookies::set(
$response,
$sessionCookie
);
}
/**
* @return array
*/
public function getCookieOptions(): array
{
return $this->cookieOptions;
}
/**
* @param array $cookieOptions
*/
public function setCookieOptions(array $cookieOptions): void
{
$this->cookieOptions = $cookieOptions;
}
/**
* Generate a session identifier.
*
* @throws Exception
*/
private function generateSessionId(): string
{
return bin2hex(random_bytes(16));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment