Core/src/Services/SessionService.php

188 lines
4.2 KiB
PHP

<?php
namespace Benzine\Services;
class SessionService implements \SessionHandlerInterface
{
protected \Redis $redis;
protected $oldID;
private int $lifetime = 43200;
private array $dirtyCheck = [];
public function __construct(\Redis $redis)
{
$this->redis = $redis;
}
public function __get($name)
{
return $this->get($name);
}
public function getLifetime(): int
{
return $this->lifetime;
}
public function setLifetime(int $lifetime): void
{
$this->lifetime = $lifetime;
}
public function initSession(): void
{
if ('cli' === PHP_SAPI || PHP_SESSION_ACTIVE === session_status()) {
return;
}
// set how long server should keep session data
ini_set('session.gc_maxlifetime', $this->getLifetime());
// set how long each client should remember their session id
session_set_cookie_params($this->getLifetime());
session_set_save_handler($this);
// Prevent session from influencing the slim headers sent back to the browser.
session_cache_limiter(null);
// Begin the Session
session_start();
}
public function close()
{
return true;
}
public function destroy($session_id)
{
$this->oldID = $session_id;
return true;
}
public function gc($maxlifetime)
{
return true;
}
public function open($save_path, $name)
{
return true;
}
public function read($session_id)
{
if ($this->useAPCU()) {
if (apcu_exists('read-'.$session_id)) {
return apcu_fetch('read-'.$session_id);
}
}
if (!empty($this->oldID)) {
$session_id = $this->oldID ? $this->oldID : $session_id;
}
$serialised = $this->redis->get("session:{$session_id}");
if (null != $serialised) {
if (!empty($this->oldID)) {
// clean up old session after regenerate
$this->redis->del("session:{$session_id}");
$this->oldID = null;
}
$result = unserialize($serialised);
} else {
$result = '';
}
if ($this->useAPCU()) {
apcu_store('read-'.$session_id, $result, 30);
} else {
$this->dirtyCheck['read-'.$session_id] = crc32($result);
}
return $result;
}
public function write($session_id, $session_data)
{
$dirty = false;
if ($this->useAPCU()) {
$dirty = crc32(apcu_fetch('read-'.$session_id)) != crc32($session_data);
} else {
$dirty = $this->dirtyCheck['read-'.$session_id] != crc32($session_data);
}
if ($dirty) {
$this->redis->set("session:{$session_id}", serialize($session_data));
$this->redis->expire("session:{$session_id}", $this->getLifetime());
}
apcu_store('read-'.$session_id, $session_data);
return true;
}
public function has(string $key): bool
{
$this->initSession();
return isset($_SESSION[$key]);
}
public function get(string $key)
{
$this->initSession();
if (isset($_SESSION[$key])) {
return unserialize($_SESSION[$key]);
}
return '';
}
public function getAll(): array
{
$this->initSession();
if (is_array($_SESSION) && count($_SESSION) > 0) {
$output = [];
foreach ($_SESSION as $k => $v) {
$output[$k] = unserialize($v);
}
return $output;
}
return [];
}
public function set(string $key, $value): bool
{
$this->initSession();
$_SESSION[$key] = serialize($value);
return true;
}
public function dispose(string $key): bool
{
$this->initSession();
if (isset($_SESSION[$key])) {
unset($_SESSION[$key]);
return true;
}
return false;
}
private function useAPCU(): bool
{
// @todo fix apcu damnit
return false;
return function_exists('apcu_store');
}
}