diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-04 02:25:28 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-04 05:43:18 +0900 |
| commit | a02fc7d728a9973a3275a0f47604081c4439b424 (patch) | |
| tree | b1a73d2eb6b17bb6c204318b58d75ae6cf46cc7a /crates/shirabe/src/event_dispatcher | |
| parent | 20f7a7826ae048d249e0d837ca6393a5f09c9ba6 (diff) | |
| download | php-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.rs | 14 |
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? { |
