Complete Phase 1: MVC, DB migrations, Auth, RBAC, Security, and Views
This commit is contained in:
148
app/Core/Session.php
Normal file
148
app/Core/Session.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user