aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-05 03:58:03 +0900
committernsfisis <nsfisis@gmail.com>2026-08-05 03:58:03 +0900
commit9b444a9a879b75a6af3d3c7ba8b9a4294574c3ec (patch)
treeb1041b55bc3ed36a391370cc2f5ececc8cb386b2 /crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher
parentbe3458128ef09b3d1074b134f2822162e077e165 (diff)
downloadphp-shirabe-9b444a9a879b75a6af3d3c7ba8b9a4294574c3ec.tar.gz
php-shirabe-9b444a9a879b75a6af3d3c7ba8b9a4294574c3ec.tar.zst
php-shirabe-9b444a9a879b75a6af3d3c7ba8b9a4294574c3ec.zip
feat(plugin): run plugin-provided commands in a worker-side application
A same-FQCN Composer\Console\Application, hand-written under the new php/runtime/ tree, hosts CommandProvider commands inside the PHP worker: PhpCommandProxy overrides run() and forwards the stringified input, so the real Symfony machinery binds, validates and executes against the live command object, while help/list render Rust-side from a definition read back at construction. Reverse \Shirabe\RustCommandStub rows let a plugin command invoke built-in commands back in the Rust process, keeping every command on the side whose helper set it was written for. Composer\EventDispatcher\Event moves from a generated stub to a dual-mode runtime class: the real BaseCommand::initialize constructs a PreCommandRunEvent natively in the worker, which a proxy-only constructor guard rejected. Its PRE_COMMAND_RUN dispatch reaches a new EventDispatcher stub whose dispatch supports the observably-no-op no-listener case and fails explicitly otherwise. The stub generator now accepts runtime-provided classes as stub bases (never as targets) and cross-checks the Application handoff property table against the real class. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher')
-rw-r--r--crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher/Event.php120
1 files changed, 120 insertions, 0 deletions
diff --git a/crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher/Event.php b/crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher/Event.php
new file mode 100644
index 00000000..0d35ac55
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/runtime/Composer/EventDispatcher/Event.php
@@ -0,0 +1,120 @@
+<?php
+
+// Shirabe's own definition of Composer\EventDispatcher\Event for the plugin worker. This class
+// lives in both worlds at once: an instance revived from a Rust handle proxies every call over
+// RPC (like a generated stub), while an instance constructed natively — real Composer code in
+// this process does `new PreCommandRunEvent(...)`, whose parent constructor lands here — is a
+// faithful in-process port of the real base class. The two modes are told apart by the
+// constructor arguments; `__shirabeRustHandleDescriptor()` returns null in native mode so the
+// wire codec registers the object in the P table instead of treating it as a Rust handle.
+
+namespace Composer\EventDispatcher;
+
+class Event implements \ShirabeRustStub
+{
+ /** @var int|null Null in native mode. */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch = 0;
+
+ /** @var string This event's name (native mode) */
+ protected $name;
+
+ /** @var string[] Arguments passed by the user, these will be forwarded to CLI script handlers (native mode) */
+ protected $args;
+
+ /** @var mixed[] Flags usable in PHP script handlers (native mode) */
+ protected $flags;
+
+ /** @var bool Whether the event should not be passed to more listeners (native mode) */
+ private $propagationStopped = false;
+
+ /**
+ * Proxy revival passes (int $rhandle, int $epoch); the real class's constructor is
+ * (string $name, array $args = [], array $flags = []).
+ */
+ public function __construct($name = null, $args = [], $flags = [])
+ {
+ if (is_int($name) && func_num_args() === 2 && is_int($args)) {
+ $this->__rhandle = $name;
+ $this->__epoch = $args;
+
+ return;
+ }
+ if (!is_string($name)) {
+ throw new \RuntimeException(
+ 'Shirabe does not support constructing ' . static::class . ' inside the plugin process without an event name'
+ );
+ }
+ $this->__rhandle = null;
+ $this->name = $name;
+ $this->args = $args;
+ $this->flags = $flags;
+ }
+
+ public function __destruct()
+ {
+ if ($this->__rhandle !== null) {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+ }
+
+ public function __shirabeRustHandleDescriptor(): ?array
+ {
+ if ($this->__rhandle === null) {
+ return null;
+ }
+
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function getName(): string
+ {
+ if ($this->__rhandle !== null) {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getName', []);
+ }
+
+ return $this->name;
+ }
+
+ public function getArguments(): array
+ {
+ if ($this->__rhandle !== null) {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getArguments', []);
+ }
+
+ return $this->args;
+ }
+
+ public function getFlags(): array
+ {
+ if ($this->__rhandle !== null) {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getFlags', []);
+ }
+
+ return $this->flags;
+ }
+
+ public function isPropagationStopped(): bool
+ {
+ if ($this->__rhandle !== null) {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isPropagationStopped', []);
+ }
+
+ return $this->propagationStopped;
+ }
+
+ public function stopPropagation(): void
+ {
+ if ($this->__rhandle !== null) {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'stopPropagation', []);
+
+ return;
+ }
+ $this->propagationStopped = true;
+ }
+}