diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-22 23:41:12 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-22 23:41:12 +0900 |
| commit | b291e714bc739262140323e08fe2fb9e91e00ee7 (patch) | |
| tree | c95f742064b9000e72b902f88e8905b4017d5dbc /crates/shirabe-external-packages/src/symfony/console | |
| parent | 99ef82a9807578c1e5749156a027949efaba75c4 (diff) | |
| download | php-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-external-packages/src/symfony/console')
3 files changed, 25 insertions, 70 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/cursor.rs b/crates/shirabe-external-packages/src/symfony/console/cursor.rs index 42efff7..811553f 100644 --- a/crates/shirabe-external-packages/src/symfony/console/cursor.rs +++ b/crates/shirabe-external-packages/src/symfony/console/cursor.rs @@ -8,25 +8,15 @@ use std::rc::Rc; #[derive(Debug)] pub struct Cursor { output: Rc<RefCell<dyn OutputInterface>>, - input: shirabe_php_shim::PhpMixed, + input: shirabe_php_shim::PhpResource, } impl Cursor { pub fn new( output: Rc<RefCell<dyn OutputInterface>>, - input: Option<shirabe_php_shim::PhpMixed>, + input: Option<shirabe_php_shim::PhpResource>, ) -> Self { - let input = input.unwrap_or_else(|| { - if shirabe_php_shim::defined("STDIN") { - // PHP uses the `STDIN` resource constant here, but the shim models it as - // `PhpResource` while this field is `PhpMixed` (which has no resource - // variant). Fall back to opening `php://input` so the value stays a - // PhpMixed stream handle. - shirabe_php_shim::fopen("php://input", "r+") - } else { - shirabe_php_shim::fopen("php://input", "r+") - } - }); + let input = input.unwrap_or(shirabe_php_shim::STDIN); Self { output, input } } @@ -228,10 +218,10 @@ impl Cursor { let stty_mode = shirabe_php_shim::shell_exec("stty -g"); shirabe_php_shim::shell_exec("stty -icanon -echo"); - shirabe_php_shim::fwrite(self.input.clone(), "\x1b[6n", 0); + shirabe_php_shim::fwrite(&self.input, "\x1b[6n", None); let code = shirabe_php_shim::trim( - shirabe_php_shim::fread(self.input.clone(), 1024) + shirabe_php_shim::fread(&self.input, 1024) .as_deref() .unwrap_or(""), None, diff --git a/crates/shirabe-external-packages/src/symfony/console/helper/question_helper.rs b/crates/shirabe-external-packages/src/symfony/console/helper/question_helper.rs index c36f77a..db9c094 100644 --- a/crates/shirabe-external-packages/src/symfony/console/helper/question_helper.rs +++ b/crates/shirabe-external-packages/src/symfony/console/helper/question_helper.rs @@ -383,7 +383,7 @@ impl QuestionHelper { ); // Read a keypress - while !feof_resource(input_stream) { + while !shirabe_php_shim::feof(input_stream) { while is_stdin && 0 == shirabe_php_shim::stream_select( &mut r, @@ -396,7 +396,7 @@ impl QuestionHelper { // Give signal handlers a chance to run r = vec![input_stream.clone()]; } - let mut c = fread_resource(input_stream, 1); + let mut c = shirabe_php_shim::fread(input_stream, 1); // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. if c.is_none() @@ -430,7 +430,7 @@ impl QuestionHelper { ret = QuestionHelper::substr(Some(&ret), 0, Some(i)); } else if c.as_deref() == Some("\u{1b}") { // Did we read an escape sequence? - let escape = fread_resource(input_stream, 2).unwrap_or_default(); + let escape = shirabe_php_shim::fread(input_stream, 2).unwrap_or_default(); let cc = format!("{}{}", c.clone().unwrap_or_default(), escape); c = Some(cc.clone()); @@ -510,7 +510,7 @@ impl QuestionHelper { "\u{f0}" => 3, _ => 0, }; - let extra = fread_resource(input_stream, len).unwrap_or_default(); + let extra = shirabe_php_shim::fread(input_stream, len).unwrap_or_default(); c = Some(format!("{}{}", cur, extra)); } @@ -649,7 +649,7 @@ impl QuestionHelper { }))); } - let value = fgets_resource(input_stream, Some(4096)); + let value = shirabe_php_shim::fgets(input_stream, Some(4096)); if STTY.load(std::sync::atomic::Ordering::SeqCst) && Terminal::has_stty_available() { shirabe_php_shim::shell_exec(&format!("stty {}", stty_mode)); @@ -764,7 +764,7 @@ impl QuestionHelper { ) -> PhpMixed { if !question.is_multiline() { let cp = self.set_io_codepage(); - let ret = fgets_resource(input_stream, Some(4096)); + let ret = shirabe_php_shim::fgets(input_stream, Some(4096)); return self.reset_io_codepage( cp, @@ -856,16 +856,16 @@ impl QuestionHelper { let uri = uri?; - let clone_stream = shirabe_php_shim::php_fopen_resource(&uri, &mode); + let clone_stream = shirabe_php_shim::fopen(&uri, &mode).ok()?; // For seekable and writable streams, add all the same data to the // cloned stream and then seek to the same offset. if matches!(seekable, PhpMixed::Bool(true)) && !["r", "rb", "rt"].contains(&mode.as_str()) { - let offset = shirabe_php_shim::ftell(input_stream); - rewind_resource(input_stream); - stream_copy_to_stream_resource(input_stream, &clone_stream); - fseek_resource(input_stream, offset); - fseek_resource(&clone_stream, offset); + let offset = shirabe_php_shim::ftell(input_stream).unwrap_or(0); + shirabe_php_shim::rewind(input_stream); + shirabe_php_shim::stream_copy_to_stream(input_stream, &clone_stream); + shirabe_php_shim::fseek(input_stream, offset, shirabe_php_shim::SEEK_SET); + shirabe_php_shim::fseek(&clone_stream, offset, shirabe_php_shim::SEEK_SET); } Some(clone_stream) @@ -882,44 +882,12 @@ fn question_as_choice_question(question: &Question) -> Option<&ChoiceQuestion> { question.as_any().downcast_ref::<ChoiceQuestion>() } -// PHP operates on raw stream resources, but the shim models `feof`/`fread`/ -// `fgets`/`fseek`/`rewind`/`stream_copy_to_stream` over `PhpMixed`, which has no -// resource variant. These thin resource-typed wrappers stand in until the shim -// grows `*_resource` counterparts (see report). Each defers to the real -// implementation once it exists. // PHP `__FILE__` magic constant. The shim's `file()` is PHP's file() function, // not the magic constant, and there is no `__FILE__` shim yet (see report). fn magic_file() -> String { todo!("magic_file: shim needs a __FILE__ magic-constant equivalent") } -fn feof_resource(_stream: &shirabe_php_shim::PhpResource) -> bool { - todo!("feof_resource: shim needs a PhpResource-based feof") -} - -fn fread_resource(_stream: &shirabe_php_shim::PhpResource, _length: i64) -> Option<String> { - todo!("fread_resource: shim needs a PhpResource-based fread") -} - -fn fgets_resource(_stream: &shirabe_php_shim::PhpResource, _length: Option<i64>) -> Option<String> { - todo!("fgets_resource: shim needs a PhpResource-based fgets") -} - -fn fseek_resource(_stream: &shirabe_php_shim::PhpResource, _offset: i64) -> i64 { - todo!("fseek_resource: shim needs a PhpResource-based fseek") -} - -fn rewind_resource(_stream: &shirabe_php_shim::PhpResource) -> bool { - todo!("rewind_resource: shim needs a PhpResource-based rewind") -} - -fn stream_copy_to_stream_resource( - _source: &shirabe_php_shim::PhpResource, - _dest: &shirabe_php_shim::PhpResource, -) -> Option<i64> { - todo!("stream_copy_to_stream_resource: shim needs a PhpResource-based stream_copy_to_stream") -} - impl HelperInterface for QuestionHelper { fn set_helper_set(&mut self, helper_set: Option<Rc<RefCell<HelperSet>>>) { self.inner.set_helper_set(helper_set); diff --git a/crates/shirabe-external-packages/src/symfony/console/terminal.rs b/crates/shirabe-external-packages/src/symfony/console/terminal.rs index e0de3f2..ac4009e 100644 --- a/crates/shirabe-external-packages/src/symfony/console/terminal.rs +++ b/crates/shirabe-external-packages/src/symfony/console/terminal.rs @@ -219,7 +219,7 @@ impl Terminal { ]), ]; - let cp = if shirabe_php_shim::function_exists("sapi_windows_cp_set") { + let _cp = if shirabe_php_shim::function_exists("sapi_windows_cp_set") { shirabe_php_shim::sapi_windows_cp_get(None) } else { 0 @@ -233,16 +233,13 @@ impl Terminal { } // $pipes[1] and $pipes[2] from proc_open's output array. - let info = shirabe_php_shim::stream_get_contents(php_pipe(&pipes, 1)); - shirabe_php_shim::fclose(php_pipe(&pipes, 1)); - shirabe_php_shim::fclose(php_pipe(&pipes, 2)); - shirabe_php_shim::proc_close(process); - - if cp != 0 { - shirabe_php_shim::sapi_windows_cp_set(cp); - } - - info + let _pipe1 = php_pipe(&pipes, 1); + let _pipe2 = php_pipe(&pipes, 2); + // TODO(phase-d): the pipe contents are read via stream_get_contents() and the pipes are + // fclose()d, but they are PHP stream resources the PhpMixed pipe list cannot hold. + todo!( + "Terminal: reading proc_open pipes needs PhpResource handles the PhpMixed pipe list cannot carry" + ); } } |
