From e521b075f6a7dacc1b90c0105df05c689aa793a4 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 15 May 2026 01:19:56 +0900 Subject: feat(port): port ErrorHandler.php --- crates/shirabe/src/util/error_handler.rs | 118 +++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'crates/shirabe/src/util/error_handler.rs') diff --git a/crates/shirabe/src/util/error_handler.rs b/crates/shirabe/src/util/error_handler.rs index d853270..2f8c699 100644 --- a/crates/shirabe/src/util/error_handler.rs +++ b/crates/shirabe/src/util/error_handler.rs @@ -1 +1,119 @@ //! ref: composer/src/Composer/Util/ErrorHandler.php + +use std::sync::{Mutex, OnceLock}; +use shirabe_php_shim::{ + debug_backtrace, error_reporting, filter_var, ini_get, is_resource, + set_error_handler, E_ALL, E_DEPRECATED, E_USER_DEPRECATED, E_WARNING, E_USER_WARNING, + FILTER_VALIDATE_BOOLEAN, PHP_EOL, STDERR, PhpMixed, ErrorException, +}; +use crate::io::io_interface::IOInterface; + +static IO: OnceLock>>> = OnceLock::new(); +static HAS_SHOWN_DEPRECATION_NOTICE: Mutex = Mutex::new(0); + +fn io() -> &'static Mutex>> { + IO.get_or_init(|| Mutex::new(None)) +} + +pub struct ErrorHandler; + +impl ErrorHandler { + pub fn handle(level: i64, message: String, file: String, line: i64) -> Result { + let is_deprecation_notice = level == E_DEPRECATED || level == E_USER_DEPRECATED; + + // error code is not included in error_reporting + if !is_deprecation_notice && 0 == (error_reporting(None) & level) { + return Ok(true); + } + + let mut message = message; + + let xdebug_scream = ini_get("xdebug.scream").unwrap_or_default(); + if filter_var(&xdebug_scream, FILTER_VALIDATE_BOOLEAN) { + message += "\n\nWarning: You have xdebug.scream enabled, the warning above may be\na legitimately suppressed error that you were not supposed to see."; + } + + if !is_deprecation_notice { + // ignore some newly introduced warnings in new php versions until dependencies + // can be fixed as we do not want to abort execution for those + if (level == E_WARNING || level == E_USER_WARNING) + && message.contains("should either be used or intentionally ignored by casting it as (void)") + { + Self::output_warning( + &format!("Ignored new PHP warning but it should be reported and fixed: {} in {}:{}", message, file, line), + true, + ); + return Ok(true); + } + + return Err(ErrorException { + message, + code: 0, + severity: level, + filename: file, + lineno: line, + }); + } + + let io_guard = io().lock().unwrap(); + if io_guard.is_some() { + let has_shown = *HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap(); + if has_shown > 0 && !io_guard.as_ref().unwrap().is_verbose() { + if has_shown == 1 { + io_guard.as_ref().unwrap().write_error("More deprecation notices were hidden, run again with `-v` to show them."); + *HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap() = 2; + } + return Ok(true); + } + *HAS_SHOWN_DEPRECATION_NOTICE.lock().unwrap() = 1; + drop(io_guard); + Self::output_warning(&format!("Deprecation Notice: {} in {}:{}", message, file, line), false); + } + + Ok(true) + } + + pub fn register(io: Option>) { + set_error_handler(|level, message, file, line| { + Self::handle(level, message.to_string(), file.to_string(), line).unwrap_or(true) + }); + error_reporting(Some(E_ALL)); + *self::io().lock().unwrap() = io; + } + + fn output_warning(message: &str, output_even_without_io: bool) { + let io_guard = io().lock().unwrap(); + if let Some(ref io) = *io_guard { + io.write_error(&format!("{}", message)); + if io.is_verbose() { + io.write_error("Stack trace:"); + let frames: Vec = debug_backtrace() + .into_iter() + .skip(2) + .filter_map(|frame| { + let line = frame.get("line").and_then(|v| v.as_int()); + let file = frame.get("file").and_then(|v| v.as_string()).map(|s| s.to_string()); + if let (Some(line), Some(file)) = (line, file) { + Some(format!(" {}:{}", file, line)) + } else { + None + } + }) + .collect(); + for frame_str in frames { + io.write_error(&frame_str); + } + } + return; + } + drop(io_guard); + + if output_even_without_io { + if is_resource(&PhpMixed::Int(STDERR)) { + shirabe_php_shim::fwrite(PhpMixed::Int(STDERR), &format!("Warning: {}{}", message, PHP_EOL), -1); + } else { + print!("Warning: {}{}", message, PHP_EOL); + } + } + } +} -- cgit v1.3.1