'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 = '
'; $html .= '
' . '⚠ GSP Debug Panel
'; // ── Fatal error block ────────────────────────────────────────────────── if ($fatal) { $fatalLabels = [ E_ERROR => 'Fatal Error', E_PARSE => 'Parse Error', E_CORE_ERROR => 'Core Error', E_COMPILE_ERROR => 'Compile Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_WARNING => 'Compile Warning', ]; $label = $fatalLabels[$fatal['type']] ?? 'Fatal'; $html .= '
' . '[' . $esc($label) . '] ' . '' . $esc($fatal['message']) . '
' . '' . $esc($shortPath($fatal['file'])) . '  line ' . (int)$fatal['line'] . '
'; } // ── Non-fatal errors ────────────────────────────────────────────────── if (!empty($nonFatals)) { $typeColors = [ 'Warning' => '#e67e22', 'Notice' => '#3498db', 'Deprecated' => '#9b59b6', 'Strict' => '#1abc9c', 'User Error' => '#e74c3c', 'User Warning' => '#e67e22', 'User Notice' => '#3498db', 'User Deprecated' => '#9b59b6', 'Recoverable Error'=> '#e74c3c', ]; foreach ($nonFatals as $err) { $color = $typeColors[$err['type']] ?? '#aaa'; $html .= '
' . '[' . $esc($err['type']) . '] ' . '' . $esc($err['message']) . '
' . '' . $esc($shortPath($err['file'])) . '  line ' . (int)$err['line'] . '
'; } } $total = count($nonFatals) + ($fatal ? 1 : 0); $html .= '
' . $total . ' issue(s) captured — DEBUG_MODE is ON. ' . 'Set define(\'DEBUG_MODE\', false); in config.inc.php to disable.
'; $html .= '
' . PHP_EOL; return $html; }