File: /var/www/rspp/bootstrap.php
<?php
declare(strict_types=1);
/**
* Bootstrap: autoload PSR-4 minimale, sessione sicura, config.
* Include questo file in cima a ogni entrypoint.
*/
$config = require __DIR__ . '/config.php';
// --- Autoloader PSR-4 minimale (namespace App\ => src/) ---
spl_autoload_register(static function (string $class): void {
$prefix = 'App\\';
if (!str_starts_with($class, $prefix)) {
return;
}
$relative = substr($class, strlen($prefix));
$file = __DIR__ . '/src/' . str_replace('\\', '/', $relative) . '.php';
if (is_file($file)) {
require $file;
}
});
// Cartella di storage allegati, FUORI dalla web root.
define('STORAGE_DIR', __DIR__ . '/storage/uploads');
// --- Sessione sicura ---
$s = $config['session'];
session_name($s['name']);
session_set_cookie_params([
'lifetime' => $s['lifetime'],
'path' => '/',
'secure' => $s['secure'],
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
// --- Connessione DB ---
$pdo = App\Database::pdo($config['db']);
/**
* Risolve il contesto tenant dalla sessione.
* Usalo nei controller: se null -> 401/redirect al login.
*/
function tenantOrRedirect(): App\TenantContext
{
$ctx = App\TenantContext::fromSession();
if ($ctx === null) {
header('Location: /login.php');
exit;
}
return $ctx;
}
/** Token CSRF per i form (login, modulistica, ecc.). */
function csrfToken(): string
{
if (empty($_SESSION['_csrf'])) {
$_SESSION['_csrf'] = bin2hex(random_bytes(32));
}
return $_SESSION['_csrf'];
}
function csrfCheck(?string $token): bool
{
return is_string($token)
&& !empty($_SESSION['_csrf'])
&& hash_equals($_SESSION['_csrf'], $token);
}
/** Escape per output HTML. */
function e(?string $v): string
{
return htmlspecialchars((string) $v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
/** Messaggi flash (sopravvivono a un redirect). */
function flash(string $msg, string $tipo = 'ok'): void
{
$_SESSION['_flash'][] = ['msg' => $msg, 'tipo' => $tipo];
}
/** @return array<int,array{msg:string,tipo:string}> e svuota la coda. */
function flashTake(): array
{
$f = $_SESSION['_flash'] ?? [];
unset($_SESSION['_flash']);
return $f;
}
/** Redirect helper. */
function redirect(string $to): never
{
header("Location: $to");
exit;
}
/** Verifica il CSRF su POST, altrimenti blocca. */
function requirePostCsrf(): void
{
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !csrfCheck($_POST['_csrf'] ?? null)) {
http_response_code(400);
exit('CSRF non valido.');
}
}