diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-15 01:19:56 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-15 19:51:17 +0900 |
| commit | e521b075f6a7dacc1b90c0105df05c689aa793a4 (patch) | |
| tree | 5d3b8a931bc1e0f8991f457e602b09b5aa39d9a1 /crates/shirabe | |
| parent | 196bf8fa15e03130c07131ed198309ae0328a1dc (diff) | |
| download | php-shirabe-e521b075f6a7dacc1b90c0105df05c689aa793a4.tar.gz php-shirabe-e521b075f6a7dacc1b90c0105df05c689aa793a4.tar.zst php-shirabe-e521b075f6a7dacc1b90c0105df05c689aa793a4.zip | |
feat(port): port ErrorHandler.php
Diffstat (limited to 'crates/shirabe')
| -rw-r--r-- | crates/shirabe/src/util/error_handler.rs | 118 |
1 files changed, 118 insertions, 0 deletions
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<Mutex<Option<Box<dyn IOInterface + Send>>>> = OnceLock::new(); +static HAS_SHOWN_DEPRECATION_NOTICE: Mutex<i64> = Mutex::new(0); + +fn io() -> &'static Mutex<Option<Box<dyn IOInterface + Send>>> { + IO.get_or_init(|| Mutex::new(None)) +} + +pub struct ErrorHandler; + +impl ErrorHandler { + pub fn handle(level: i64, message: String, file: String, line: i64) -> Result<bool, ErrorException> { + 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("<warning>More deprecation notices were hidden, run again with `-v` to show them.</warning>"); + *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<Box<dyn IOInterface + Send>>) { + 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!("<warning>{}</warning>", message)); + if io.is_verbose() { + io.write_error("<warning>Stack trace:</warning>"); + let frames: Vec<String> = 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!("<warning> {}:{}</warning>", 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); + } + } + } +} |
