1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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);
}
}
}
}
|