From c5fc34a106a706ac12925c4df273a926811d260b Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 24 Jun 2026 03:09:17 +0900 Subject: feat(process): redesign proc_* on PhpResource process handles proc_open/proc_close/proc_get_status/proc_terminate represented the process handle as a PhpMixed, which cannot hold a live child process or its pipes, so they were stubs or todo!(). Model the handle as a new PhpResource::Process variant and child pipes as a StreamBacking::Pipe, with a native Descriptor enum for descriptorspec; proc_open now returns io::Result and fills pipes as IndexMap. Rewire the Symfony Process pipes and the Console terminal/cursor onto the new types, removing the "PhpMixed cannot carry a PhpResource" todo!()s. The remaining todo!()s are genuine syscall leaves (proc_terminate signal delivery, stream_select, stream_set_blocking, posix_kill, pty, fd>=3) left unimplemented since no syscall crate is introduced. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe-php-shim/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'crates/shirabe-php-shim/src/lib.rs') diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs index 8eb8f2f..113c9d7 100644 --- a/crates/shirabe-php-shim/src/lib.rs +++ b/crates/shirabe-php-shim/src/lib.rs @@ -346,6 +346,7 @@ pub enum PhpResource { Stdout, Stderr, Stream(std::rc::Rc>), + Process(std::rc::Rc>), } /// Combined capability of every seekable byte stream backing. Both `std::fs::File` @@ -361,6 +362,8 @@ pub enum StreamBacking { /// TODO(phase-d): `php://temp/maxmemory:N` spills to a temp file past N bytes; /// the threshold is ignored here and everything stays in memory. Memory(std::io::Cursor>), + /// A child process pipe created by `proc_open`. Half-duplex and not seekable. + Pipe(ChildPipe), } impl StreamBacking { @@ -368,10 +371,55 @@ impl StreamBacking { match self { StreamBacking::File(f) => f, StreamBacking::Memory(c) => c, + StreamBacking::Pipe(p) => p, } } } +/// One end of a child process pipe. Each variant supports only the direction PHP +/// allows for it; the unsupported operations return `ErrorKind::Unsupported` so the +/// `ReadWriteSeek` contract is satisfied without pretending pipes are seekable. +#[derive(Debug)] +pub enum ChildPipe { + In(std::process::ChildStdin), + Out(std::process::ChildStdout), + Err(std::process::ChildStderr), +} + +impl std::io::Read for ChildPipe { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match self { + ChildPipe::Out(o) => o.read(buf), + ChildPipe::Err(e) => e.read(buf), + ChildPipe::In(_) => Err(std::io::Error::from(std::io::ErrorKind::Unsupported)), + } + } +} + +impl std::io::Write for ChildPipe { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match self { + ChildPipe::In(i) => i.write(buf), + ChildPipe::Out(_) | ChildPipe::Err(_) => { + Err(std::io::Error::from(std::io::ErrorKind::Unsupported)) + } + } + } + + fn flush(&mut self) -> std::io::Result<()> { + match self { + ChildPipe::In(i) => i.flush(), + ChildPipe::Out(_) | ChildPipe::Err(_) => Ok(()), + } + } +} + +impl std::io::Seek for ChildPipe { + fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result { + Err(std::io::Error::from(std::io::ErrorKind::Unsupported)) + } +} + #[derive(Debug)] pub struct StreamState { pub(crate) backing: StreamBacking, -- cgit v1.3.1