aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-external-packages/src/symfony/console/signal_registry
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe-external-packages/src/symfony/console/signal_registry')
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs3
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs100
2 files changed, 103 insertions, 0 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs b/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs
new file mode 100644
index 0000000..a07ca01
--- /dev/null
+++ b/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs
@@ -0,0 +1,3 @@
+pub mod signal_registry;
+
+pub use signal_registry::*;
diff --git a/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs b/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs
new file mode 100644
index 0000000..bd6b989
--- /dev/null
+++ b/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs
@@ -0,0 +1,100 @@
+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);
+ }
+ }
+}