aboutsummaryrefslogtreecommitdiffhomepage
path: root/docs/dev/php-rpc.md
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-30 23:01:25 +0900
committernsfisis <nsfisis@gmail.com>2026-08-30 23:09:56 +0900
commitd3bc3354c9705dfc6dc5e9b9adb5eb64d41e4c49 (patch)
treea745ecc3403104d5a34f29964362e239e8f5675b /docs/dev/php-rpc.md
parent057f3b8de26293319e265c1d86d9a1153124f3c7 (diff)
downloadphp-shirabe-d3bc3354c9705dfc6dc5e9b9adb5eb64d41e4c49.tar.gz
php-shirabe-d3bc3354c9705dfc6dc5e9b9adb5eb64d41e4c49.tar.zst
php-shirabe-d3bc3354c9705dfc6dc5e9b9adb5eb64d41e4c49.zip
feat(plugin): serve ProcessExecutor as a proxy stub
Composer reaches this class two ways: the object graph hands one out through Composer::getLoop()->getProcessExecutor(), and plugins write `new ProcessExecutor($io)` freely. Both bind to a Rust-side entity, so the timeout the run shares -- seeded from process-timeout and rewritten while the run is in flight -- has one value instead of one per world, and the executor can still be passed to the classes that take one (`new Filesystem($process)`). Three things the stub generator was missing came with it: - By-ref parameters. The call carries their positions and the answer carries what each holds afterwards; a position the answer omits was never assigned to, which is what PHP does with an untouched by-ref parameter. ProcessExecutor::execute is the only one on a proxied class. - Argument arity, reproduced where the real body reads func_num_args(). execute($cmd) forwards the child's output and execute($cmd, $out) captures it, and nothing but the argument count separates the two. - Static methods that cannot run in the worker. One that reads a static property the Rust side owns, or that reaches a guarded class, forwards through __shirabeCallStatic instead of being materialized. That also fixes Filesystem::isLocalPath and getPlatformPath, whose materialized bodies called the guarded Composer\Util\Platform. The async surface stays an explicit error. executeAsync resolves its promise with a Symfony Process, whose proc_open() resource and pipes belong to whichever process called start(), so a Rust-side spawn has none to hand back; running the real start() in the worker needs a promise representation that crosses the boundary unresolved. The fixture project drives the whole synchronous surface from plugin code and compares the trace against upstream Composer byte for byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'docs/dev/php-rpc.md')
-rw-r--r--docs/dev/php-rpc.md20
1 files changed, 17 insertions, 3 deletions
diff --git a/docs/dev/php-rpc.md b/docs/dev/php-rpc.md
index eb93db19..aebf0097 100644
--- a/docs/dev/php-rpc.md
+++ b/docs/dev/php-rpc.md
@@ -184,10 +184,24 @@ Notable internal helpers:
The runtime service endpoint (handle 0) answers `__shirabe_find_file` (autoload lookups),
`__shirabe_run_rust_command` — the reverse half of the two-world command split: a
`\Shirabe\RustCommandStub` forwards its stringified input here and the built-in command runs in
-the Rust process, against the Rust-side application state — and `__shirabeConstruct`, which
+the Rust process, against the Rust-side application state — `__shirabeConstruct`, which
allocates the Rust entity behind a `new SomeProxiedClass(...)` written by plugin code and
-answers with `[rhandle, epoch]`. Classes whose entity Rust cannot build are an explicit error
-naming the class.
+answers with `[rhandle, epoch]`, and `__shirabeCallStatic`, which takes `[class, method, args]`
+and runs a static method no handle identifies a receiver for. Classes and static methods Rust
+cannot answer are an explicit error naming them.
+
+### By-ref parameters
+
+`out_param_positions` names the by-ref parameters of the called method, as the caller declared
+them; the answering `Return` carries `out_params`, a map from the same positions to the value
+each parameter holds afterwards, and the caller assigns those back into its own variables. A
+position the answer leaves out was never assigned to, which is what PHP does with an untouched
+by-ref parameter — so an out-param is genuinely optional rather than defaulting to null.
+
+A generated stub fills both halves mechanically from the real signature
+(`docs/dev/plugin-stub-generation.md`). `Composer\Util\ProcessExecutor::execute` is the method
+this exists for: it is the only by-ref parameter on Composer's public surface that belongs to a
+proxied class.
## Proxy stubs, runtime classes and guards