aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim/src/lib.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-22 23:41:12 +0900
committernsfisis <nsfisis@gmail.com>2026-06-22 23:41:12 +0900
commitb291e714bc739262140323e08fe2fb9e91e00ee7 (patch)
treec95f742064b9000e72b902f88e8905b4017d5dbc /crates/shirabe-php-shim/src/lib.rs
parent99ef82a9807578c1e5749156a027949efaba75c4 (diff)
downloadphp-shirabe-b291e714bc739262140323e08fe2fb9e91e00ee7.tar.gz
php-shirabe-b291e714bc739262140323e08fe2fb9e91e00ee7.tar.zst
php-shirabe-b291e714bc739262140323e08fe2fb9e91e00ee7.zip
feat(php-shim): implement fopen-family stream API on PhpResource
Redesign PhpResource into a real stream handle (File/Memory backing with tracked position, eof, closed state) and unify the whole fopen family (fopen/fwrite/fread/fgets/fgetc/feof/fclose/ftell/fseek/rewind/fstat/ ftruncate/fflush and stream_get_contents/stream_copy_to_stream) on &PhpResource, replacing the split PhpMixed/PhpResource APIs and their todo!() stubs. fopen now returns Result; read functions stay String for now (TODO(phase-e) to move to byte strings). Propagate the signatures through callers: Process stdout/stderr, Cursor input, curl header/body handles (extracted into typed maps keyed by job id), Filesystem copy/safe_copy/files_are_equal, BufferIO, error_handler, platform, perforce, zip. The proc_open pipe paths cannot carry a PhpResource in a PhpMixed list, so they are left as todo!() with notes.
Diffstat (limited to 'crates/shirabe-php-shim/src/lib.rs')
-rw-r--r--crates/shirabe-php-shim/src/lib.rs51
1 files changed, 50 insertions, 1 deletions
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index 10e89fe..267c3b7 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -345,5 +345,54 @@ pub enum PhpResource {
Stdin,
Stdout,
Stderr,
- File(std::rc::Rc<std::cell::RefCell<std::fs::File>>),
+ Stream(std::rc::Rc<std::cell::RefCell<StreamState>>),
+}
+
+/// Combined capability of every seekable byte stream backing. Both `std::fs::File`
+/// and `std::io::Cursor<Vec<u8>>` satisfy it, so a stream can be driven uniformly.
+pub trait ReadWriteSeek: std::io::Read + std::io::Write + std::io::Seek {}
+impl<T: std::io::Read + std::io::Write + std::io::Seek> ReadWriteSeek for T {}
+
+#[derive(Debug)]
+pub enum StreamBacking {
+ /// A real file on disk (also `/dev/null`); the OS tracks the position.
+ File(std::fs::File),
+ /// `php://memory` and `php://temp` — an in-memory growable buffer.
+ /// 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>>),
+}
+
+impl StreamBacking {
+ pub(crate) fn as_rws(&mut self) -> &mut dyn ReadWriteSeek {
+ match self {
+ StreamBacking::File(f) => f,
+ StreamBacking::Memory(c) => c,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct StreamState {
+ pub(crate) backing: StreamBacking,
+ /// Whether the mode opened the stream for reading.
+ pub(crate) readable: bool,
+ /// Whether the mode opened the stream for writing.
+ pub(crate) writable: bool,
+ /// Set once a read attempt sees end-of-stream, mirroring PHP's `feof()` which
+ /// only reports true after a read has hit the end; cleared by a seek.
+ pub(crate) eof: bool,
+ pub(crate) closed: bool,
+}
+
+impl StreamState {
+ pub(crate) fn new(backing: StreamBacking, readable: bool, writable: bool) -> PhpResource {
+ PhpResource::Stream(std::rc::Rc::new(std::cell::RefCell::new(StreamState {
+ backing,
+ readable,
+ writable,
+ eof: false,
+ closed: false,
+ })))
+ }
}