getMessage(), $exception->getFile(), $exception->getLine(), $exception->getTraceAsString() ); self::log($message, E_ERROR); // Send error response if headers not sent if (!headers_sent()) { self::sendErrorResponse( 'An unexpected error occurred', 500, $exception ); } } /** * Handle fatal errors on shutdown */ public static function handleShutdown(): void { $error = error_get_last(); if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { $message = sprintf( "Fatal Error: %s in %s on line %d", $error['message'], $error['file'], $error['line'] ); self::log($message, E_ERROR); if (!headers_sent()) { self::sendErrorResponse('A fatal error occurred', 500); } } } /** * Log an error message * * @param string $message Error message * @param int $level Error level * @param array $context Additional context */ public static function log(string $message, int $level = E_USER_NOTICE, array $context = []): void { $timestamp = date('Y-m-d H:i:s'); $levelName = self::getErrorTypeName($level); $logMessage = "[$timestamp] [$levelName] $message"; if (!empty($context)) { $logMessage .= " | Context: " . json_encode($context); } error_log($logMessage); } /** * Send a JSON error response * * @param string $message User-facing error message * @param int $httpCode HTTP status code * @param Throwable|null $exception Original exception (for debug info) */ public static function sendErrorResponse(string $message, int $httpCode = 500, ?Throwable $exception = null): void { http_response_code($httpCode); if (!headers_sent()) { header('Content-Type: application/json'); } $response = [ 'success' => false, 'error' => $message ]; // Add debug info in development (check for debug mode) if (isset($GLOBALS['config']['DEBUG']) && $GLOBALS['config']['DEBUG'] && $exception) { $response['debug'] = [ 'type' => get_class($exception), 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine() ]; } echo json_encode($response); exit; } /** * Send a validation error response * * @param array $errors Array of validation errors * @param string $message Overall error message */ public static function sendValidationError(array $errors, string $message = 'Validation failed'): void { http_response_code(422); if (!headers_sent()) { header('Content-Type: application/json'); } echo json_encode([ 'success' => false, 'error' => $message, 'validation_errors' => $errors ]); exit; } /** * Send a not found error response * * @param string $message Error message */ public static function sendNotFoundError(string $message = 'Resource not found'): void { self::sendErrorResponse($message, 404); } /** * Send an unauthorized error response * * @param string $message Error message */ public static function sendUnauthorizedError(string $message = 'Authentication required'): void { self::sendErrorResponse($message, 401); } /** * Send a forbidden error response * * @param string $message Error message */ public static function sendForbiddenError(string $message = 'Access denied'): void { self::sendErrorResponse($message, 403); } /** * Get error type name from error number * * @param int $errno Error number * @return string Error type name */ private static function getErrorTypeName(int $errno): string { $types = [ E_ERROR => 'ERROR', E_WARNING => 'WARNING', E_PARSE => 'PARSE', 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', E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR', E_DEPRECATED => 'DEPRECATED', E_USER_DEPRECATED => 'USER_DEPRECATED', ]; return $types[$errno] ?? 'UNKNOWN'; } /** * Get recent error log entries * * @param int $lines Number of lines to return * @return array Log entries */ public static function getRecentErrors(int $lines = 50): array { if (self::$logFile === null || !file_exists(self::$logFile)) { return []; } $file = file(self::$logFile); if ($file === false) { return []; } return array_slice($file, -$lines); } }