0, 'failed' => 0, 'warnings' => 0, ]; function ok(string $msg): void { global $results; $results['passed']++; echo " " . COLOR_GREEN . "✅" . COLOR_RESET . " {$msg}\n"; } function fail(string $msg, ?string $detail = null): void { global $results; $results['failed']++; echo " " . COLOR_RED . "❌" . COLOR_RESET . " {$msg}"; if ($detail !== null) { echo COLOR_RED . " — {$detail}" . COLOR_RESET; } echo "\n"; } function warn(string $msg, ?string $detail = null): void { global $results; $results['warnings']++; echo " " . COLOR_YELLOW . "⚠️ " . COLOR_RESET . " {$msg}"; if ($detail !== null) { echo COLOR_YELLOW . " — {$detail}" . COLOR_RESET; } echo "\n"; } function heading(string $title): void { echo "\n" . COLOR_BOLD . COLOR_CYAN . "═══ {$title} ═══" . COLOR_RESET . "\n\n"; } function v(string $msg): void { global $verbose; if ($verbose) { echo " " . COLOR_YELLOW . "[verbose]" . COLOR_RESET . " {$msg}\n"; } } // ───────────────────────────────────────────────────────────────────────────── // 2. Parse .env (standalone, no framework) // ───────────────────────────────────────────────────────────────────────────── function parseEnv(string $path): array { if (!file_exists($path)) { return []; } $env = []; $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { $line = trim($line); if ($line === '' || str_starts_with($line, '#')) { continue; } if (str_contains($line, '=')) { $parts = explode('=', $line, 2); $key = trim($parts[0]); $value = trim($parts[1] ?? ''); // Rimuovi eventuali apici if ((str_starts_with($value, '"') && str_ends_with($value, '"')) || (str_starts_with($value, "'") && str_ends_with($value, "'"))) { $value = substr($value, 1, -1); } // Risolvi variabili ${...} $value = preg_replace_callback('/\$\{(\w+)\}/', function ($m) use ($env) { return $env[$m[1]] ?? $m[0]; }, $value); $env[$key] = $value; } } return $env; } // ───────────────────────────────────────────────────────────────────────────── // 3. Check funzioni // ───────────────────────────────────────────────────────────────────────────── echo COLOR_BOLD . "\n" . " ╔════════════════════════════════════════════╗\n" . " ║ Glastree — Diagnosi Installazione ║\n" . " ╚════════════════════════════════════════════╝\n" . COLOR_RESET; echo "\n Data: " . date('Y-m-d H:i:s') . " | PID: " . getmypid() . "\n"; // ───────────────────────────────────────────────────────────────────────────── // 3.1 PHP Version & Extensions // ───────────────────────────────────────────────────────────────────────────── heading('PHP Version & Extensions'); $phpVersion = PHP_VERSION; $phpMajorMinor = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; if (version_compare($phpVersion, '8.2', '>=')) { ok("PHP version: {$phpVersion}"); } else { fail("PHP version: {$phpVersion} — richiesta ≥ 8.2"); } $requiredExts = [ 'json', 'mbstring', 'openssl', 'PDO', 'pdo_mysql', 'fileinfo', 'xml', 'ctype', 'bcmath', 'zip', 'gd', 'intl', 'curl', 'session', ]; $optionalExts = [ 'imap' => 'Sync email IMAP', 'imagick' => 'Gestione immagini avanzata', 'redis' => 'Cache/sessione Redis', 'sodium' => 'Crittografia (Laravel 11+)', 'sockets' => 'Connessioni socket', 'tokenizer' => 'Laravel optimization', ]; foreach ($requiredExts as $ext) { if (extension_loaded($ext)) { ok("Estensione {$ext}: OK"); } else { fail("Estensione {$ext}: NON TROVATA", "Richiesta da Laravel"); } } foreach ($optionalExts as $ext => $label) { if (extension_loaded($ext)) { ok("Estensione {$ext}: OK ({$label})"); } else { warn("Estensione {$ext}: NON TROVATA ({$label})"); } } // INI settings $iniChecks = [ 'max_execution_time' => [30, '60+ consigliato per import CSV'], 'memory_limit' => ['128M', '128M minimo, 256M+ consigliato'], 'upload_max_filesize' => ['20M', '20M minimo per CSV/ICS'], 'post_max_size' => ['20M', '20M minimo per upload'], 'max_input_vars' => [1000, '1000+ per form con molti campi'], 'max_input_time' => [60, '60+ per upload file'], ]; heading('PHP INI Settings'); foreach ($iniChecks as $iniKey => [$minVal, $note]) { $actual = ini_get($iniKey); $ok = false; if (is_numeric($minVal) && is_numeric($actual)) { $ok = (int) $actual >= (int) $minVal; } elseif (is_string($minVal)) { // Comparazione bytes $actualBytes = returnBytes($actual); $minBytes = returnBytes($minVal); $ok = $actualBytes >= $minBytes; } if ($ok) { ok("{$iniKey} = {$actual}"); } else { warn("{$iniKey} = {$actual} (minimo: {$minVal})", $note); } } function returnBytes(string $val): int { $val = trim($val); $last = strtolower($val[-1]); $num = (int) $val; return match ($last) { 'g' => $num * 1024 * 1024 * 1024, 'm' => $num * 1024 * 1024, 'k' => $num * 1024, default => $num, }; } // ───────────────────────────────────────────────────────────────────────────── // 3.2 .env File // ───────────────────────────────────────────────────────────────────────────── heading('File .env'); $envPath = BASE_DIR . '/.env'; if (!file_exists($envPath)) { fail('.env NON TROVATO', "Cercato in: {$envPath}"); $env = []; } elseif (!is_readable($envPath)) { fail('.env NON LEGGIBILE', "Permessi: " . decoct(fileperms($envPath) & 0777)); $env = []; } else { ok(".env trovato e leggibile"); $env = parseEnv($envPath); v("Variabili parsate: " . count($env)); } // ───────────────────────────────────────────────────────────────────────────── // 3.3 Configurazione .env // ───────────────────────────────────────────────────────────────────────────── heading('Configurazione .env'); // APP_KEY if (empty($env['APP_KEY'])) { fail('APP_KEY non impostato'); } elseif (!str_starts_with($env['APP_KEY'], 'base64:')) { fail('APP_KEY formato errato', "Deve iniziare con 'base64:'"); } else { $decoded = base64_decode(substr($env['APP_KEY'], 7), true); if ($decoded !== false && strlen($decoded) === 32) { ok("APP_KEY: valido (32 bytes)"); } else { fail('APP_KEY: decodifica fallita o lunghezza errata', "Deve essere 32 bytes dopo decodifica base64"); } } // APP_URL if (empty($env['APP_URL'])) { fail('APP_URL non impostato'); } elseif (!str_starts_with($env['APP_URL'], 'http://') && !str_starts_with($env['APP_URL'], 'https://')) { fail('APP_URL non valido', $env['APP_URL']); } else { ok("APP_URL: {$env['APP_URL']}"); // Verifica reachability via curl $ch = curl_init($env['APP_URL']); curl_setopt_array($ch, [ CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_NOBODY => true, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($response === false) { fail("APP_URL non raggiungibile via HTTP", $curlError); } else { ok("APP_URL raggiungibile (HTTP {$httpCode})"); } } // FORCE_HTTPS vs SESSION_SECURE_COOKIE $forceHttps = ($env['FORCE_HTTPS'] ?? 'false') === 'true'; $sessionSecure = ($env['SESSION_SECURE_COOKIE'] ?? 'false') === 'true'; if ($forceHttps && !$sessionSecure) { warn('FORCE_HTTPS=true ma SESSION_SECURE_COOKIE non impostato', 'Il cookie di sessione potrebbe non essere inviato su HTTP'); } elseif (!$forceHttps && $sessionSecure) { warn('SESSION_SECURE_COOKIE=true ma FORCE_HTTPS non attivo', 'Il cookie di sessione non verrà inviato su HTTP'); } else { ok("FORCE_HTTPS / SESSION_SECURE_COOKIE: coerente"); } // SESSION_DRIVER $sessionDriver = $env['SESSION_DRIVER'] ?? 'file'; if (in_array($sessionDriver, ['file', 'database', 'redis', 'memcached', 'cookie', 'dynamodb'])) { ok("SESSION_DRIVER: {$sessionDriver}"); } else { fail("SESSION_DRIVER: {$sessionDriver} sconosciuto"); } // SESSION_DOMAIN check if (!empty($env['SESSION_DOMAIN']) && $env['SESSION_DOMAIN'] !== 'null') { $parsedUrl = parse_url($env['APP_URL'] ?? 'http://localhost'); $urlHost = $parsedUrl['host'] ?? 'localhost'; if (!str_contains($env['SESSION_DOMAIN'], $urlHost)) { warn("SESSION_DOMAIN ({$env['SESSION_DOMAIN']}) non corrisponde a APP_URL ({$urlHost})", 'Il cookie di sessione potrebbe non essere inviato'); } else { ok("SESSION_DOMAIN: {$env['SESSION_DOMAIN']}"); } } else { ok("SESSION_DOMAIN: null (usa dominio richiesta)"); } // DB_CONNECTION $dbConnection = $env['DB_CONNECTION'] ?? 'mysql'; if (in_array($dbConnection, ['mysql', 'mariadb', 'sqlite', 'pgsql', 'sqlsrv'])) { ok("DB_CONNECTION: {$dbConnection}"); } else { fail("DB_CONNECTION: {$dbConnection} sconosciuto"); } // APP_DEBUG in produzione $appEnv = $env['APP_ENV'] ?? 'production'; $appDebug = ($env['APP_DEBUG'] ?? 'false') === 'true'; if ($appEnv === 'production' && $appDebug) { warn('APP_DEBUG=true in ambiente production', 'Disabilita in produzione: espone errori sensibili'); } else { ok("APP_DEBUG: " . ($appDebug ? 'true' : 'false') . " in ambiente {$appEnv}"); } // ───────────────────────────────────────────────────────────────────────────── // 3.4 Directory Permissions // ───────────────────────────────────────────────────────────────────────────── heading('Directory & Permessi'); $writeDirs = [ 'storage' => BASE_DIR . '/storage', 'storage/logs' => BASE_DIR . '/storage/logs', 'storage/framework' => BASE_DIR . '/storage/framework', 'storage/framework/sessions' => BASE_DIR . '/storage/framework/sessions', 'storage/framework/views' => BASE_DIR . '/storage/framework/views', 'storage/framework/cache' => BASE_DIR . '/storage/framework/cache', 'storage/framework/cache/data' => BASE_DIR . '/storage/framework/cache/data', 'storage/app/public' => BASE_DIR . '/storage/app/public', 'storage/app/public/logos' => BASE_DIR . '/storage/app/public/logos', 'storage/app/public/documenti/eventi' => BASE_DIR . '/storage/app/public/documenti/eventi', 'storage/app/private' => BASE_DIR . '/storage/app/private', 'bootstrap/cache' => BASE_DIR . '/bootstrap/cache', ]; foreach ($writeDirs as $label => $dir) { if (!file_exists($dir)) { warn("{$label}: directory NON ESISTE"); continue; } if (!is_dir($dir)) { fail("{$label}: NON è una directory"); continue; } $perms = decoct(fileperms($dir) & 0777); $owner = function_exists('posix_getpwuid') ? posix_getpwuid(fileowner($dir))['name'] ?? fileowner($dir) : fileowner($dir); if (is_writable($dir)) { ok("{$label}: {$perms} proprietario: {$owner} — SCRIVIBILE"); } else { fail("{$label}: {$perms} proprietario: {$owner} — NON SCRIVIBILE", "Chmod: chmod -R 775 " . str_replace(BASE_DIR . '/', '', $dir)); } } // ───────────────────────────────────────────────────────────────────────────── // 3.5 Symlink Check // ───────────────────────────────────────────────────────────────────────────── heading('Symlink'); $publicStorage = BASE_DIR . '/public/storage'; $target = $publicStorage; if (!file_exists($publicStorage)) { fail("public/storage: NON ESISTE", "Esegui: php artisan storage:link"); v("Path: {$publicStorage}"); } elseif (!is_link($publicStorage)) { fail("public/storage: NON è un symlink (è una directory/file)"); } else { $readTarget = readlink($publicStorage); $expected = realpath(BASE_DIR . '/storage/app/public'); // Se readlink restituisce percorso assoluto, usalo direttamente $resolved = str_starts_with($readTarget, '/') ? realpath($readTarget) : realpath(dirname($publicStorage) . '/' . $readTarget); if ($resolved === $expected) { $displayTarget = str_starts_with($readTarget, '/') ? str_replace(BASE_DIR . '/', '', $readTarget) : $readTarget; ok("public/storage → {$displayTarget} (puntamento corretto)"); } else { fail("public/storage → {$readTarget}", "Dovrebbe puntare a: " . str_replace(BASE_DIR . '/', '', $expected)); } } // ───────────────────────────────────────────────────────────────────────────── // 3.6 Session Test // ───────────────────────────────────────────────────────────────────────────── heading('Sessione — Test scrittura/lettura'); $sessionDir = BASE_DIR . '/storage/framework/sessions'; if (!is_writable($sessionDir)) { fail("Directory session NON scrivibile — test saltato"); } else { $testFile = $sessionDir . '/.diagnose_test_' . getmypid(); $testVal = 'diagnose_' . bin2hex(random_bytes(8)); $written = file_put_contents($testFile, $testVal); if ($written === false) { fail("Scrittura file sessione: FALLITA", $testFile); } else { $read = file_get_contents($testFile); unlink($testFile); if ($read === $testVal) { ok("Scrittura/lettura file sessione: OK"); } else { fail("Scrittura/lettura file sessione: DATI NON CORRISPONDONO", "Letto: {$read}, atteso: {$testVal}"); } } } // ───────────────────────────────────────────────────────────────────────────── // 3.7 Database Connectivity (standalone via PDO) // ───────────────────────────────────────────────────────────────────────────── heading('Database'); $dbDriver = $env['DB_CONNECTION'] ?? 'mysql'; $dbHost = $env['DB_HOST'] ?? '127.0.0.1'; $dbPort = $env['DB_PORT'] ?? '3306'; $dbName = $env['DB_DATABASE'] ?? ''; $dbUser = $env['DB_USERNAME'] ?? ''; $dbPass = $env['DB_PASSWORD'] ?? ''; if (empty($dbName)) { fail("DB_DATABASE non impostato nel .env"); } elseif ($dbDriver === 'sqlite') { $sqlitePath = $env['DB_DATABASE_SQLITE'] ?? 'database/database.sqlite'; $absPath = str_starts_with($sqlitePath, '/') ? $sqlitePath : BASE_DIR . '/' . $sqlitePath; if (file_exists($absPath)) { $perms = decoct(fileperms($absPath) & 0777); ok("SQLite database trovato: {$sqlitePath} ({$perms})"); } else { fail("SQLite database NON TROVATO: {$sqlitePath}", "Cercato in: {$absPath}"); } } else { $dsn = "{$dbDriver}:host={$dbHost};port={$dbPort};dbname={$dbName};charset=utf8mb4"; v("Tentativo connessione: {$dsn} user={$dbUser}"); try { $pdo = new PDO($dsn, $dbUser, $dbPass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5, ]); $serverInfo = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION); $driverInfo = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); ok("Connessione DB: OK (driver: {$driverInfo}, versione: {$serverInfo})"); // Verifica esistenza tabelle principali $stmt = $pdo->query("SHOW TABLES"); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN); $expectedTables = ['individui', 'gruppi', 'eventi', 'users', 'tags', 'mailing_lists', 'documenti']; $foundTables = []; foreach ($expectedTables as $table) { if (in_array($table, $tables, true)) { $foundTables[] = $table; } } if (count($foundTables) === count($expectedTables)) { ok("Tabelle principali: tutte presenti (" . implode(', ', $foundTables) . ")"); } else { $missing = array_diff($expectedTables, $foundTables); warn("Tabelle mancanti: " . implode(', ', $missing), "Esegui: php artisan migrate"); } // Verifica conteggio records foreach (['users', 'individui', 'gruppi', 'eventi', 'tags'] as $table) { if (in_array($table, $tables, true)) { $count = $pdo->query("SELECT COUNT(*) FROM `{$table}`")->fetchColumn(); ok("Tabella {$table}: {$count} record"); } } // Verifica engine InnoDB try { $engines = $pdo->query("SHOW TABLE STATUS WHERE Engine IS NOT NULL")->fetchAll(PDO::FETCH_ASSOC); $nonInnoDb = []; foreach ($engines as $row) { if (($row['Engine'] ?? '') !== 'InnoDB' && ($row['Engine'] ?? '') !== '') { $nonInnoDb[] = $row['Name'] . ' (' . ($row['Engine'] ?? 'unknown') . ')'; } } if (empty($nonInnoDb)) { ok("Engine: tutte le tabelle usano InnoDB"); } else { warn("Engine non-InnoDB: " . implode(', ', $nonInnoDb), "InnoDB raccomandato per integrità referenziale"); } } catch (PDOException $e) { v("SHOW TABLE STATUS non supportato: " . $e->getMessage()); } } catch (PDOException $e) { $errorMsg = $e->getMessage(); // Maschera password in output $errorSafe = str_replace($dbPass, '****', $errorMsg); fail("Connessione DB FALLITA", $errorSafe); // Diagnostica if ($e->getCode() === 2002) { v("Host '{$dbHost}:{$dbPort}' non raggiungibile. Verifica DB_HOST e DB_PORT."); } elseif ($e->getCode() === 1045) { v("Accesso negato per utente '{$dbUser}'."); } elseif ($e->getCode() === 1049) { v("Database '{$dbName}' non esiste."); } elseif ($e->getCode() === 1044) { v("Utente '{$dbUser}' non ha accesso al database '{$dbName}'."); } } // Verifica estensione PDO nel caso la connessione non sia stata testata if (!extension_loaded('pdo_mysql') && $dbDriver === 'mysql') { fail("PDO MySQL non installato", "Installa: apt install php-mysql"); } } // ───────────────────────────────────────────────────────────────────────────── // 3.8 Bootstrap/app.php middleware check // ───────────────────────────────────────────────────────────────────────────── heading('Configurazione Middleware (bootstrap/app.php)'); $appPath = BASE_DIR . '/bootstrap/app.php'; if (!file_exists($appPath)) { fail("bootstrap/app.php NON TROVATO"); } else { $appContent = file_get_contents($appPath); $warnedDoubles = false; // Check ForceHttps registration if (str_contains($appContent, 'ForceHttps')) { ok("ForceHttps middleware: registrato"); } else { warn("ForceHttps middleware: NON registrato", "FORCE_HTTPS=true nel .env ma middleware non aggiunto a bootstrap/app.php"); } // Check TrustProxies if (str_contains($appContent, 'trustProxies')) { ok("TrustProxies: configurato"); } else { warn("TrustProxies: NON configurato", "Può causare problemi con HTTPS dietro proxy"); } // Check for duplicated web middleware $webAppend = []; if (preg_match('/\$middleware->web\(append:\s*\[([^\]]*)\]/s', $appContent, $m)) { $block = $m[1]; preg_match_all('/class\s*:\s*[a-z\\\]*\\\([a-zA-Z]+)/i', $block, $classMatches); $webAppend = $classMatches[1] ?? []; } $defaultWeb = ['EncryptCookies', 'AddQueuedCookiesToResponse', 'StartSession', 'ShareErrorsFromSession']; foreach ($defaultWeb as $mw) { if (in_array($mw, $webAppend)) { warn("Middleware duplicato in web(append): {$mw}", "Già presente nel default stack di Laravel 11. Può causare 419 su login."); $warnedDoubles = true; } } if (!$warnedDoubles) { ok("Middleware web group: nessuna duplicazione"); } } // ───────────────────────────────────────────────────────────────────────────── // 3.9 Vendor & Autoload // ───────────────────────────────────────────────────────────────────────────── heading('Vendor & Autoload'); $vendorAutoload = BASE_DIR . '/vendor/autoload.php'; if (file_exists($vendorAutoload)) { ok("vendor/autoload.php: presente"); $vendorSize = filesize(BASE_DIR . '/vendor/'); if (is_dir(BASE_DIR . '/vendor/composer')) { ok("composer: installato"); } else { fail("vendor/composer/ mancante", "Esegui: composer install"); } } else { fail("vendor/autoload.php NON TROVATO", "Esegui: composer install --no-dev"); } // Package.json / node_modules if (is_dir(BASE_DIR . '/node_modules')) { ok("node_modules: presente"); } else { warn("node_modules: assente", "Esegui: npm install (non necessario in produzione se public/build/ è presente)"); } // Check public/build if (is_dir(BASE_DIR . '/public/build')) { ok("public/build: presente (asset Vite compilati)"); } else { warn("public/build: assente", "Esegui: npm run build (gli asset AdminLTE potrebbero non funzionare)"); } // Check artisan if (file_exists(BASE_DIR . '/artisan')) { ok("artisan: presente"); if (!is_executable(BASE_DIR . '/artisan')) { warn("artisan: NON eseguibile", "chmod +x artisan"); } } else { fail("artisan NON TROVATO"); } // ───────────────────────────────────────────────────────────────────────────── // 3.10 CSRF Test (HTTP) // ───────────────────────────────────────────────────────────────────────────── heading('Test CSRF (via HTTP)'); $baseUrl = $env['APP_URL'] ?? 'http://localhost'; $loginUrl = rtrim($baseUrl, '/') . '/login'; $loginUrlHttp = str_replace('https://', 'http://', $loginUrl); v("Test login page: {$loginUrl}"); v("Test HTTP variant: {$loginUrlHttp}"); $ch = curl_init($loginUrl); curl_setopt_array($ch, [ CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_HEADER => true, CURLOPT_COOKIEJAR => '/tmp/glastree_diagnose_cookies_' . getmypid() . '.txt', CURLOPT_COOKIEFILE => '/tmp/glastree_diagnose_cookies_' . getmypid() . '.txt', ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = is_string($response) ? substr($response, 0, $headerSize) : ''; $body = is_string($response) ? substr($response, $headerSize) : ''; curl_close($ch); if ($response === false || $body === false || $body === '') { fail("Login page non raggiungibile", $curlError); warn("Test CSRF saltato — APP_URL non raggiungibile da questo server"); } else { ok("Login page raggiungibile (HTTP {$httpCode})"); // Extract CSRF token $token = null; if (preg_match('/]*name=["\']_token["\'][^>]*value=["\']([^"\']+)["\']/i', $body, $m)) { $token = $m[1]; ok("CSRF token estratto dalla pagina login"); v("Token (primi 20 char): " . substr($token, 0, 20) . '...'); } else { fail("CSRF token NON TROVATO nella pagina login", "@csrf potrebbe non funzionare correttamente"); } // Try POST to login with token if ($token !== null) { $ch2 = curl_init($loginUrl); curl_setopt_array($ch2, [ CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_HEADER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query([ '_token' => $token, 'email' => 'test@example.com', 'password' => 'wrongpassword', ]), CURLOPT_COOKIEJAR => '/tmp/glastree_diagnose_cookies_' . getmypid() . '.txt', CURLOPT_COOKIEFILE => '/tmp/glastree_diagnose_cookies_' . getmypid() . '.txt', ]); $postResponse = curl_exec($ch2); $postHttpCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE); $postHeaders = substr($postResponse, 0, curl_getinfo($ch2, CURLINFO_HEADER_SIZE)); curl_close($ch2); if ($postHttpCode === 419) { fail("POST login → 419 Page Expired", "CSRO token non riconosciuto. Possibili cause: sessione non persistente," . " SESSION_SECURE_COOKIE errato, middleware duplicati, permessi session."); } elseif ($postHttpCode === 302 || $postHttpCode === 200) { ok("POST login → HTTP {$postHttpCode} (419 non verificato)"); v("Se 200: la pagina restituita probabilmente contiene errori di validazione (normale)"); } else { warn("POST login → HTTP {$postHttpCode} (inaspettato)"); } } } // Cleanup cookies $cookieFile = '/tmp/glastree_diagnose_cookies_' . getmypid() . '.txt'; if (file_exists($cookieFile)) { unlink($cookieFile); } // ───────────────────────────────────────────────────────────────────────────── // 3.11 Disk Space // ───────────────────────────────────────────────────────────────────────────── heading('Spazio Disco'); $total = disk_total_space(BASE_DIR); $free = disk_free_space(BASE_DIR); $used = $total - $free; $pctUsed = round(($used / $total) * 100, 1); function formatBytes(int|float $bytes): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $i = 0; while ($bytes >= 1024 && $i < count($units) - 1) { $bytes /= 1024; $i++; } return round($bytes, 1) . ' ' . $units[$i]; } ok("Spazio totale: " . formatBytes($total)); ok("Spazio libero: " . formatBytes($free)); if ($pctUsed > 90) { fail("Spazio utilizzato: {$pctUsed}% — CRITICO"); } elseif ($pctUsed > 80) { warn("Spazio utilizzato: {$pctUsed}%"); } else { ok("Spazio utilizzato: {$pctUsed}%"); } // ───────────────────────────────────────────────────────────────────────────── // 4. Summary // ───────────────────────────────────────────────────────────────────────────── heading('Riepilogo'); echo "\n"; echo " " . COLOR_GREEN . "✅ Passed: {$results['passed']}" . COLOR_RESET . "\n"; echo " " . COLOR_YELLOW . "⚠️ Warnings: {$results['warnings']}" . COLOR_RESET . "\n"; echo " " . COLOR_RED . "❌ Failed: {$results['failed']}" . COLOR_RESET . "\n"; echo "\n"; if ($results['failed'] > 0) { echo COLOR_BOLD . COLOR_RED . " ⛔ PROBLEMI CRITICI TROVATI — Risolvere prima di proseguire." . COLOR_RESET . "\n\n"; exit(1); } elseif ($results['warnings'] > 0) { echo COLOR_BOLD . COLOR_YELLOW . " ⚠️ Warning presenti — Verificare i punti segnalati." . COLOR_RESET . "\n\n"; exit(0); } else { echo COLOR_BOLD . COLOR_GREEN . " ✅ Tutti i controlli superati. Installazione OK." . COLOR_RESET . "\n\n"; exit(0); }