'E_ERROR', E_WARNING => 'E_WARNING', E_PARSE => 'E_PARSE', E_NOTICE => 'E_NOTICE', E_CORE_ERROR => 'E_CORE_ERROR', E_CORE_WARNING => 'E_CORE_WARNING', E_COMPILE_ERROR => 'E_COMPILE_ERROR', E_COMPILE_WARNING => 'E_COMPILE_WARNING', E_USER_ERROR => 'E_USER_ERROR', E_USER_WARNING => 'E_USER_WARNING', E_USER_NOTICE => 'E_USER_NOTICE', E_STRICT => 'E_STRICT', E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', E_DEPRECATED => 'E_DEPRECATED', E_USER_DEPRECATED => 'E_USER_DEPRECATED', ]; private static array $errorTypeLabels = [ E_ERROR => 'Fatal Error', E_WARNING => 'Warning', E_PARSE => 'Parse Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Strict Standards', E_RECOVERABLE_ERROR => 'Recoverable Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', ]; private static array $fatalTypes = [ E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, ]; public static function handleError($errno, $errstr, $errfile, $errline) { if (!(error_reporting() & $errno)) { return; } // Only persist fatal/critical errors — skip warnings, notices, deprecated, strict if (in_array($errno, self::$fatalTypes)) { self::persistError($errno, $errstr, $errfile, $errline); self::logError($errno, $errstr, $errfile, $errline); } self::displayError($errno, $errstr, $errfile, $errline); return true; } public static function handleException($exception) { $trace = $exception->getTraceAsString(); self::persistError(E_ERROR, $exception->getMessage(), $exception->getFile(), $exception->getLine(), $trace, 'Exception'); self::logError(E_ERROR, $exception->getMessage(), $exception->getFile(), $exception->getLine()); self::displayError(E_ERROR, $exception->getMessage(), $exception->getFile(), $exception->getLine(), $trace); } public static function handleShutdown() { $error = error_get_last(); if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) { self::persistError($error['type'], $error['message'], $error['file'], $error['line']); self::logError($error['type'], $error['message'], $error['file'], $error['line']); self::displayError($error['type'], $error['message'], $error['file'], $error['line']); } } // ─── JSON flat-file persistence ─────────────────────────────────────────── private static function persistError($errno, $errstr, $errfile, $errline, $trace = null, $forcedType = null) { try { $errorType = $forcedType ?? (self::$errorTypeMap[$errno] ?? 'UNKNOWN'); $userId = null; $orgId = null; if (isset($_SESSION['login'])) { $userId = $_SESSION['login']['userid'] ?? null; $orgId = $_SESSION['login']['org_id'] ?? null; } $url = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? '') . ($_SERVER['REQUEST_URI'] ?? ''); $method = $_SERVER['REQUEST_METHOD'] ?? null; $ipAddress = $_SERVER['REMOTE_ADDR'] ?? null; $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null; $referer = $_SERVER['HTTP_REFERER'] ?? null; $entry = [ 'id' => uniqid('err_', true), 'userid' => $userId, 'org_id' => $orgId, 'errortype' => $errorType, 'errorcode' => (string)$errno, 'url' => substr($url, 0, 512), 'file' => substr($errfile, 0, 512), 'line' => (int)$errline, 'fullerror' => $errstr, 'trace' => $trace, 'method' => $method ? substr($method, 0, 10) : null, 'ip_address' => $ipAddress ? substr($ipAddress, 0, 45) : null, 'user_agent' => $userAgent ? substr($userAgent, 0, 512) : null, 'referer' => $referer ? substr($referer, 0, 512) : null, 'status' => 'unresolved', 'createdate' => date('Y-m-d H:i:s'), ]; $logPath = __DIR__ . '/errors.json'; $entries = []; if (file_exists($logPath)) { $raw = file_get_contents($logPath); $decoded = json_decode($raw, true); if (is_array($decoded)) { $entries = $decoded; } } array_unshift($entries, $entry); if (count($entries) > 1000) { $entries = array_slice($entries, 0, 1000); } file_put_contents($logPath, json_encode($entries, JSON_PRETTY_PRINT), LOCK_EX); } catch (\Throwable $e) { // Silently fail — never let error logging crash the app error_log('ErrorHandler::persistError failed: ' . $e->getMessage()); } } // ─── File logging ───────────────────────────────────────────────────────── private static function logError($errno, $errstr, $errfile, $errline) { $message = date('[Y-m-d H:i:s]') . " Error: [$errno] $errstr in $errfile on line $errline\n"; error_log($message, 3, __DIR__ . '/app_errors.log'); } // ─── Display (debug mode only) ──────────────────────────────────────────── private static function displayError($errno, $errstr, $errfile, $errline, $trace = null) { $errorType = self::$errorTypeLabels[$errno] ?? 'Unknown Error'; if (defined('DEBUG') && DEBUG) { echo "
Message: " . htmlspecialchars($errstr) . "
"; echo "File: " . htmlspecialchars($errfile) . "
"; echo "Line: " . htmlspecialchars((string)$errline) . "
"; if ($trace) { echo "" . htmlspecialchars($trace) . ""; } echo "
";
echo "URL: " . htmlspecialchars($_SERVER['REQUEST_URI'] ?? '') . "\n";
echo "Method: " . htmlspecialchars($_SERVER['REQUEST_METHOD'] ?? '') . "\n";
echo "Time: " . date('Y-m-d H:i:s') . "\n";
echo "IP: " . htmlspecialchars($_SERVER['REMOTE_ADDR'] ?? '') . "\n";
echo "