From 3f3ad76eafcc5513000ac7526c74c2009bb390d8 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Tue, 4 Aug 2026 04:31:51 +0900 Subject: feat(plugin): remove subscribed listeners when a plugin is removed Wire removePlugin to EventDispatcher::removeListener now that subscriber listeners carry the plugin's P-table handle: PHP's $candidate[0] === $listener identity check maps to phandle equality on Callable::PhpMethod. Co-Authored-By: Claude Fable 5 --- crates/shirabe/src/plugin/plugin_manager.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'crates/shirabe/src/plugin/plugin_manager.rs') diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index a3810d0c..aaee7d5a 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -772,9 +772,19 @@ impl PluginManager { .borrow_mut() .deactivate(self.composer_full(), self.io.clone())?; - // TODO(plugin): remove_listener accepts any callable/object in PHP; here we have - // a plugin instance and need to translate to a Callable, which is not portable - // without runtime reflection. + // PHP passes the plugin object itself; its cross-RPC identity (the P-table handle) is + // carried by the subscriber accessor, and `addSubscriber` is the only source of + // listeners capturing a plugin object today. + // TODO(plugin): a plugin registering `[$this, 'method']` listeners directly through an + // EventDispatcher proxy would have no removal path here; no such RPC surface exists yet. + let subscriber_handle = removed + .borrow() + .as_event_subscriber() + .map(|s| s.subscriber_handle()); + if let Some(handle) = subscriber_handle { + let event_dispatcher = self.composer_full().borrow().get_event_dispatcher(); + event_dispatcher.borrow_mut().remove_listener(&handle); + } Ok(()) } -- cgit v1.3.1