diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-24 03:09:17 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-24 03:09:17 +0900 |
| commit | c5fc34a106a706ac12925c4df273a926811d260b (patch) | |
| tree | 24e798c4a00b32aa1a1f155b705d02fba9895875 /crates/shirabe-php-shim/src/lib.rs | |
| parent | b2b321a26f6a628ee8e6757eb8577613e2024e70 (diff) | |
| download | php-shirabe-c5fc34a106a706ac12925c4df273a926811d260b.tar.gz php-shirabe-c5fc34a106a706ac12925c4df273a926811d260b.tar.zst php-shirabe-c5fc34a106a706ac12925c4df273a926811d260b.zip | |
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<i64, PhpResource>.
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) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim/src/lib.rs')
| -rw-r--r-- | crates/shirabe-php-shim/src/lib.rs | 48 |
1 files changed, 48 insertions, 0 deletions
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<std::cell::RefCell<StreamState>>), + Process(std::rc::Rc<std::cell::RefCell<process::ProcessState>>), } /// 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<Vec<u8>>), + /// 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<usize> { + 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<usize> { + 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<u64> { + Err(std::io::Error::from(std::io::ErrorKind::Unsupported)) + } +} + #[derive(Debug)] pub struct StreamState { pub(crate) backing: StreamBacking, |
