aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
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 /crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
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 'crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php')
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php176
1 files changed, 176 insertions, 0 deletions
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php b/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
new file mode 100644
index 00000000..dfa1f0d2
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Util/ProcessExecutor.php
@@ -0,0 +1,176 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Util\ProcessExecutor: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Util;
+
+use Composer\IO\IOInterface;
+use React\Promise\PromiseInterface;
+
+class ProcessExecutor implements \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ /**
+ * Binds a stub the registry built for an existing entity. Proxy instantiation bypasses
+ * the constructor, which belongs to plugin code building a new entity instead.
+ */
+ public function __shirabeBind(int $rhandle, int $epoch): void
+ {
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function __clone()
+ {
+ // PHP has already shallow-copied this stub, so both copies would point at one
+ // entity and release it twice. The Rust side clones the entity instead, applying
+ // whatever __clone semantics the real class defines, and this copy rebinds to the
+ // fresh handle. Entities without clone semantics answer with an explicit error.
+ [$this->__rhandle, $this->__epoch] = \ShirabeRpcRuntime::callRust($this->__rhandle, '__shirabeClone', []);
+ \ShirabeRustObjectRegistry::adopt($this->__rhandle, $this);
+ }
+
+ public function __get($name)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__get', [$name]);
+ }
+
+ public function __set($name, $value): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__set', [$name, $value]);
+ }
+
+ public function __isset($name): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__isset', [$name]);
+ }
+
+ public function __unset($name): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__unset', [$name]);
+ }
+
+ public function __construct(?IOInterface $io = null)
+ {
+ [$this->__rhandle, $this->__epoch] = \ShirabeRpcRuntime::callRust(0, '__shirabeConstruct', [static::class, [$io]]);
+ \ShirabeRustObjectRegistry::adopt($this->__rhandle, $this);
+ }
+
+ private const STATUS_QUEUED = 1;
+ private const STATUS_STARTED = 2;
+ private const STATUS_COMPLETED = 3;
+ private const STATUS_FAILED = 4;
+ private const STATUS_ABORTED = 5;
+ private const BUILTIN_CMD_COMMANDS = [
+ 'assoc', 'break', 'call', 'cd', 'chdir', 'cls', 'color', 'copy', 'date',
+ 'del', 'dir', 'echo', 'endlocal', 'erase', 'exit', 'for', 'ftype', 'goto',
+ 'help', 'if', 'label', 'md', 'mkdir', 'mklink', 'move', 'path', 'pause',
+ 'popd', 'prompt', 'pushd', 'rd', 'rem', 'ren', 'rename', 'rmdir', 'set',
+ 'setlocal', 'shift', 'start', 'time', 'title', 'type', 'ver', 'vol',
+ ];
+ private const GIT_CMDS_NEED_GIT_DIR = [
+ ['show'],
+ ['log'],
+ ['branch'],
+ ['remote', 'set-url'],
+ ];
+
+ // Forwarded rather than materialized: it reads the static property $timeout, whose value the Rust side owns.
+ public static function getTimeout(): int
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'getTimeout', []]);
+ }
+
+ // Forwarded rather than materialized: it reads the static property $timeout, whose value the Rust side owns.
+ public static function setTimeout(int $timeout): void
+ {
+ \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'setTimeout', [$timeout]]);
+ }
+
+ // Forwarded rather than materialized: it references Composer\Util\Platform, which a guard shadows in the worker.
+ public static function escape($argument): string
+ {
+ return \ShirabeRpcRuntime::callRust(0, '__shirabeCallStatic', [self::class, 'escape', [$argument]]);
+ }
+
+ public function execute($command, &$output = null, ?string $cwd = null): int
+ {
+ $__args = [$command, $output, $cwd];
+ array_splice($__args, func_num_args());
+ $__out = [];
+ $__result = \ShirabeRpcRuntime::callRust($this->__rhandle, 'execute', $__args, [1], $__out);
+ if (array_key_exists(1, $__out)) {
+ $output = $__out[1];
+ }
+ return $__result;
+ }
+
+ public function executeTty($command, ?string $cwd = null): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'executeTty', [$command, $cwd]);
+ }
+
+ public function executeAsync($command, ?string $cwd = null): PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'executeAsync', [$command, $cwd]);
+ }
+
+ public function setMaxJobs(int $maxJobs): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setMaxJobs', [$maxJobs]);
+ }
+
+ public function resetMaxJobs(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'resetMaxJobs', []);
+ }
+
+ public function wait($index = null): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'wait', [$index]);
+ }
+
+ public function enableAsync(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'enableAsync', []);
+ }
+
+ public function countActiveJobs($index = null): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'countActiveJobs', [$index]);
+ }
+
+ public function splitLines(?string $output): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'splitLines', [$output]);
+ }
+
+ public function getErrorOutput(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getErrorOutput', []);
+ }
+
+ public function requiresGitDirEnv($command): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'requiresGitDirEnv', [$command]);
+ }
+}