149 lines
3.4 KiB
PHP
149 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Core;
|
|
|
|
class Session
|
|
{
|
|
private const FLASH_KEY = 'flash_messages';
|
|
|
|
public function __construct()
|
|
{
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
// Set secure session parameters
|
|
session_start([
|
|
'cookie_httponly' => true,
|
|
'cookie_secure' => false, // Set to true if HTTPS is enforced (e.g. on production)
|
|
'cookie_samesite' => 'Lax',
|
|
]);
|
|
}
|
|
|
|
// Mark existing flash messages to be deleted next request
|
|
$this->ageFlashMessages();
|
|
}
|
|
|
|
/**
|
|
* Set a session value.
|
|
*/
|
|
public function set(string $key, mixed $value): void
|
|
{
|
|
$_SESSION[$key] = $value;
|
|
}
|
|
|
|
/**
|
|
* Get a session value.
|
|
*/
|
|
public function get(string $key, mixed $default = null): mixed
|
|
{
|
|
return $_SESSION[$key] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* Remove a session value.
|
|
*/
|
|
public function remove(string $key): void
|
|
{
|
|
unset($_SESSION[$key]);
|
|
}
|
|
|
|
/**
|
|
* Check if a session key exists.
|
|
*/
|
|
public function has(string $key): bool
|
|
{
|
|
return isset($_SESSION[$key]);
|
|
}
|
|
|
|
/**
|
|
* Set a flash message (available only for the next request).
|
|
*/
|
|
public function setFlash(string $key, string $message): void
|
|
{
|
|
$_SESSION[self::FLASH_KEY][$key] = [
|
|
'value' => $message,
|
|
'remove' => false
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get a flash message.
|
|
*/
|
|
public function getFlash(string $key, ?string $default = null): ?string
|
|
{
|
|
return $_SESSION[self::FLASH_KEY][$key]['value'] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* Get all flash messages.
|
|
*/
|
|
public function getFlashes(): array
|
|
{
|
|
$flashes = [];
|
|
foreach ($_SESSION[self::FLASH_KEY] ?? [] as $key => $flash) {
|
|
$flashes[$key] = $flash['value'];
|
|
}
|
|
return $flashes;
|
|
}
|
|
|
|
/**
|
|
* Age flash messages at start of request.
|
|
*/
|
|
private function ageFlashMessages(): void
|
|
{
|
|
$flashMessages = $_SESSION[self::FLASH_KEY] ?? [];
|
|
foreach ($flashMessages as $key => &$flash) {
|
|
if ($flash['remove']) {
|
|
unset($flashMessages[$key]);
|
|
} else {
|
|
$flash['remove'] = true;
|
|
}
|
|
}
|
|
$_SESSION[self::FLASH_KEY] = $flashMessages;
|
|
}
|
|
|
|
/**
|
|
* Generate or fetch CSRF token.
|
|
*/
|
|
public function getCsrfToken(): string
|
|
{
|
|
$token = $this->get('csrf_token');
|
|
if (!$token) {
|
|
$token = bin2hex(random_bytes(32));
|
|
$this->set('csrf_token', $token);
|
|
}
|
|
return $token;
|
|
}
|
|
|
|
/**
|
|
* Validate CSRF token.
|
|
*/
|
|
public function validateCsrfToken(?string $token): bool
|
|
{
|
|
if (!$token) {
|
|
return false;
|
|
}
|
|
$storedToken = $this->get('csrf_token');
|
|
return hash_equals($storedToken, $token);
|
|
}
|
|
|
|
/**
|
|
* Destroy the session.
|
|
*/
|
|
public function destroy(): void
|
|
{
|
|
$_SESSION = [];
|
|
if (ini_get("session.use_cookies")) {
|
|
$params = session_get_cookie_params();
|
|
setcookie(
|
|
session_name(),
|
|
'',
|
|
time() - 42000,
|
|
$params["path"],
|
|
$params["domain"],
|
|
$params["secure"],
|
|
$params["httponly"]
|
|
);
|
|
}
|
|
session_destroy();
|
|
}
|
|
}
|