HEX
Server: nginx/1.27.0
System: Linux ns31201788 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User: www-data (33)
PHP: 8.1.31
Disabled: NONE
Upload Files
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.');
    }
}