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
|
//! ref: composer/vendor/symfony/console/SignalRegistry/SignalRegistry.php
use indexmap::IndexMap;
/// A signal handler receives the signal number and whether a further handler follows.
pub type SignalHandler = Box<dyn Fn(i64, bool)>;
pub struct SignalRegistry {
// signal number => list of handlers
signal_handlers: IndexMap<i64, Vec<SignalHandler>>,
}
impl std::fmt::Debug for SignalRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SignalRegistry")
.field("signal_handlers", &self.signal_handlers.keys())
.finish_non_exhaustive()
}
}
impl Default for SignalRegistry {
fn default() -> Self {
Self::new()
}
}
impl SignalRegistry {
pub fn new() -> Self {
if shirabe_php_shim::function_exists("pcntl_async_signals") {
shirabe_php_shim::pcntl_async_signals(true);
}
Self {
signal_handlers: IndexMap::new(),
}
}
pub fn register(&mut self, signal: i64, signal_handler: SignalHandler) {
if !self.signal_handlers.contains_key(&signal) {
let previous_callback = shirabe_php_shim::pcntl_signal_get_handler(signal);
if shirabe_php_shim::is_callable(&previous_callback) {
// $this->signalHandlers[$signal][] = $previousCallback;
// The previous handler is an opaque PHP callable obtained from pcntl;
// it is invoked through the runtime callable mechanism.
self.signal_handlers
.entry(signal)
.or_default()
.push(Box::new(move |signal, has_next| {
shirabe_php_shim::call_php_callable(
&previous_callback,
&[
shirabe_php_shim::PhpMixed::Int(signal),
shirabe_php_shim::PhpMixed::Bool(has_next),
],
);
}));
}
}
self.signal_handlers
.entry(signal)
.or_default()
.push(signal_handler);
// pcntl_signal($signal, [$this, 'handle'])
// TODO(plugin): the PHP callback `[$this, 'handle']` captures the registry
// instance. Wiring this object method as a C-level signal handler requires the
// runtime callable mechanism; see review notes.
shirabe_php_shim::pcntl_signal(signal, shirabe_php_shim::PhpMixed::Null);
}
pub fn is_supported() -> bool {
if !shirabe_php_shim::function_exists("pcntl_signal") {
return false;
}
if shirabe_php_shim::explode(
",",
&shirabe_php_shim::ini_get("disable_functions").unwrap_or_default(),
)
.contains(&"pcntl_signal".to_string())
{
return false;
}
true
}
pub fn handle(&self, signal: i64) {
let handlers = match self.signal_handlers.get(&signal) {
Some(handlers) => handlers,
None => return,
};
let count = handlers.len();
for (i, signal_handler) in handlers.iter().enumerate() {
let has_next = i != count - 1;
signal_handler(signal, has_next);
}
}
}
|