aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/event_dispatcher
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-04 02:25:28 +0900
committernsfisis <nsfisis@gmail.com>2026-08-04 05:43:18 +0900
commita02fc7d728a9973a3275a0f47604081c4439b424 (patch)
treeb1a73d2eb6b17bb6c204318b58d75ae6cf46cc7a /crates/shirabe/src/event_dispatcher
parent20f7a7826ae048d249e0d837ca6393a5f09c9ba6 (diff)
downloadphp-shirabe-a02fc7d728a9973a3275a0f47604081c4439b424.tar.gz
php-shirabe-a02fc7d728a9973a3275a0f47604081c4439b424.tar.zst
php-shirabe-a02fc7d728a9973a3275a0f47604081c4439b424.zip
feat(plugin): activate plugins through the PHP RPC worker
Implement the remainder of PluginManager::registerPackage: the plugin autoload map is built by the ported createLoader/parseAutoloads and served to the worker over the existing reverse-RPC autoloader, files entries go through a composerRequire-equivalent glue call, and already-defined classes take the upstream _composer_tmp rename/eval path. Instantiation uses the new NewObject/CallPhpMethod lanes backed by a P table in the worker; PhpPluginProxy adapts the resulting handle to PluginInterface, with $composer/$io exposed to plugin callbacks via an R table (unsupported methods stay explicit errors). Hand-written proxy stubs cover Composer, PartialComposer and the IO hierarchy, and the stub autoloader is re-prepended after loading the Composer PHP runtime so its vendor autoloader cannot shadow proxied FQCNs. FilesystemRepository::write now mirrors InstalledVersions::reload into a running worker (class_exists-guarded, so an unloaded class keeps its upstream lazy-load behavior), removing the previously undefined observation window. The installer pipeline passes the installed repository as a shared handle instead of a long-lived `&mut dyn`: plugin registration runs inside InstallationManager::execute and re-enters the same local repository through the RepositoryManager, which would panic on the RefCell re-borrow under the old shape. PluginInterface lifecycle methods now take an owned ComposerHandle (plugins retain $composer past the call) and return anyhow::Result (PHP plugin code may throw); the plugin list uses shared ownership so the identity comparison of removePlugin survives the dual storage in registeredPlugins, matching PHP reference semantics. Ports the activate/upgrade/uninstall tests of PluginInstallerTest, serialized across the shared worker process whose persistent class table is exactly what exercises the rename path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/event_dispatcher')
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs14
1 files changed, 7 insertions, 7 deletions
diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
index 2097c83d..f5b85ca1 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -596,7 +596,7 @@ impl EventDispatcher {
// The user's command class extends Symfony's Command, so the child
// process needs the real symfony/console classes before it can even
// autoload the user class.
- self.ensure_composer_php_runtime()?;
+ Self::ensure_composer_php_runtime()?;
if !self.php_runtime_bool(
"class_exists",
vec![PluginValue::string(class_name.clone())],
@@ -699,7 +699,7 @@ try {{
"false"
},
);
- self.ensure_script_autoloader()?;
+ Self::ensure_script_autoloader()?;
let mut dispatcher = ScriptRpcDispatcher {
loader: self.loader.clone(),
event: None,
@@ -1054,7 +1054,7 @@ try {{
}));
};
- self.ensure_script_autoloader()?;
+ Self::ensure_script_autoloader()?;
let rhandle = shirabe_php_rpc::alloc_rhandle();
let mut dispatcher = ScriptRpcDispatcher {
loader: self.loader.clone(),
@@ -1435,7 +1435,7 @@ try {{
/// Makes the worker's script-class autoloader active, so class queries and script execution
/// in the child can resolve classes through the Rust-side [`ClassLoader`] built by
/// [`Self::make_autoloader`].
- fn ensure_script_autoloader(&self) -> anyhow::Result<()> {
+ pub(crate) fn ensure_script_autoloader() -> anyhow::Result<()> {
unwrap_php_result(call_function(
"__shirabe_enable_script_autoloader",
Vec::new(),
@@ -1445,7 +1445,7 @@ try {{
/// Loads the Composer PHP runtime (symfony/console and friends) into the worker, needed
/// before a `scripts` Command class can be autoloaded and hosted.
- fn ensure_composer_php_runtime(&self) -> anyhow::Result<()> {
+ pub(crate) fn ensure_composer_php_runtime() -> anyhow::Result<()> {
// TODO(plugin): the real PHP classes are taken from a Composer checkout for now; how
// they ship with a released Shirabe binary is part of the plugin distribution work.
let autoload = Self::composer_php_runtime_autoload().ok_or_else(|| {
@@ -1484,7 +1484,7 @@ try {{
/// Runs a boolean runtime query (`class_exists`, `is_a`, ...) inside the PHP worker, with
/// the script autoloader active so the query can trigger class loading.
fn php_runtime_bool(&self, function: &str, args: Vec<PluginValue>) -> anyhow::Result<bool> {
- self.ensure_script_autoloader()?;
+ Self::ensure_script_autoloader()?;
let mut dispatcher = ScriptRpcDispatcher {
loader: self.loader.clone(),
event: None,
@@ -1616,7 +1616,7 @@ fn runtime_throw(message: String) -> PhpThrow {
/// Collapses the two failure lanes of an RPC call into `anyhow`: the callers here treat a PHP
/// exception raised during a runtime query as fatal for the current dispatch.
-fn unwrap_php_result(
+pub(crate) fn unwrap_php_result(
outcome: anyhow::Result<Result<PluginValue, PhpThrow>>,
) -> anyhow::Result<PluginValue> {
match outcome? {