'Warning', E_NOTICE => 'Notice', E_DEPRECATED => 'Deprecated', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_USER_DEPRECATED => 'User Deprecated', E_STRICT => 'Strict', E_RECOVERABLE_ERROR => 'Recoverable Error', ]; $type = $levels[$errno] ?? "Error (#{$errno})"; $GLOBALS['_gsp_debug_errors'][] = [ 'type' => $type, 'message' => $errstr, 'file' => $errfile, 'line' => $errline, ]; // Do NOT suppress the built-in handler (allows display_errors to also show inline) return false; }); /** * Shutdown handler – catches fatal / parse / compile errors and renders them. * Also renders the non-fatal error panel collected above. */ register_shutdown_function(function (): void { if (!defined('DEBUG_MODE') || !DEBUG_MODE) { return; } $fatal = error_get_last(); $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING]; $hasFatal = $fatal && in_array($fatal['type'], $fatalTypes, true); $nonFatalErrors = $GLOBALS['_gsp_debug_errors'] ?? []; if (!$hasFatal && empty($nonFatalErrors)) { return; } // Attempt to end any open output buffers so our panel appears at the bottom while (ob_get_level() > 0) { ob_end_flush(); } echo gsp_debug_render_panel($hasFatal ? $fatal : null, $nonFatalErrors); }); /** * Renders the styled debug panel HTML. * * @param array|null $fatal Fatal error array from error_get_last(), or null * @param array $nonFatals Array of non-fatal error entries * @return string */ function gsp_debug_render_panel(?array $fatal, array $nonFatals): string { $docRoot = defined('DOCUMENT_ROOT') ? DOCUMENT_ROOT : ($_SERVER['DOCUMENT_ROOT'] ?? ''); $stripLen = strlen($docRoot); $shortPath = static function (string $path) use ($docRoot, $stripLen): string { if ($stripLen > 0 && strpos($path, $docRoot) === 0) { return '…' . substr($path, $stripLen); } return $path; }; $esc = static fn(string $s): string => htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $html = '
define(\'DEBUG_MODE\', false); in config.inc.php to disable.