From 3a0d9340810a8808d963135a884f50d08442ac67 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Thu, 25 Jun 2026 16:27:53 +0900 Subject: test: port 59 autoload/vcs/installer/util/command tests; fix output capture Port autoload_generator (24), bitbucket (14), suggested_packages (11), git_driver (6), archive_manager (3), and a bump command test. Fix the ApplicationTester output-capture root cause (php://memory streams must be readable regardless of fopen mode). Implement posix_getuid/geteuid, the PCRE 'A' anchored modifier, php_strip_whitespace, stream_get_wrappers, is_callable scalars; fix preg_quote angle-bracket escaping and class-map parser regexes. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe-php-shim/src/string.rs | 140 +++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 4 deletions(-) (limited to 'crates/shirabe-php-shim/src/string.rs') diff --git a/crates/shirabe-php-shim/src/string.rs b/crates/shirabe-php-shim/src/string.rs index 48c72f4..9a82fe8 100644 --- a/crates/shirabe-php-shim/src/string.rs +++ b/crates/shirabe-php-shim/src/string.rs @@ -1327,10 +1327,142 @@ pub fn addcslashes(_string: &str, _charlist: &str) -> String { String::from_utf8_lossy(&out).into_owned() } -pub fn php_strip_whitespace(_path: &str) -> String { - // TODO(phase-d): PHP strips comments and redundant whitespace using the PHP tokenizer; no PHP - // tokenizer is available in the shim. - todo!() +pub fn php_strip_whitespace(path: &str) -> String { + // PHP `php_strip_whitespace()` tokenizes the source and re-emits it with comments removed and + // each run of whitespace collapsed to a single space. There is no PHP tokenizer in the shim, so + // this is a hand-written lexer that reproduces the observable effect for the cases the class-map + // generator depends on: it preserves single-quoted, double-quoted, backtick and heredoc/nowdoc + // string contents verbatim while dropping `//`, `#` and `/* */` comments and squeezing + // whitespace. On any read failure it returns an empty string, mirroring `@php_strip_whitespace`. + let contents = match std::fs::read(path) { + Ok(bytes) => bytes, + Err(_) => return String::new(), + }; + + let b = contents; + let n = b.len(); + let mut out: Vec = Vec::with_capacity(n); + let mut i = 0usize; + + let push_space = |out: &mut Vec| { + if !out.is_empty() && *out.last().unwrap() != b' ' { + out.push(b' '); + } + }; + + while i < n { + let c = b[i]; + + // Line comments: // ... and # ... + if c == b'/' && i + 1 < n && b[i + 1] == b'/' { + i += 2; + while i < n && b[i] != b'\n' { + i += 1; + } + push_space(&mut out); + continue; + } + if c == b'#' { + i += 1; + while i < n && b[i] != b'\n' { + i += 1; + } + push_space(&mut out); + continue; + } + // Block comments: /* ... */ + if c == b'/' && i + 1 < n && b[i + 1] == b'*' { + i += 2; + while i + 1 < n && !(b[i] == b'*' && b[i + 1] == b'/') { + i += 1; + } + i += 2; + push_space(&mut out); + continue; + } + // String literals: '...', "...", `...` + if c == b'\'' || c == b'"' || c == b'`' { + out.push(c); + i += 1; + while i < n { + let d = b[i]; + out.push(d); + if d == b'\\' && i + 1 < n { + out.push(b[i + 1]); + i += 2; + continue; + } + i += 1; + if d == c { + break; + } + } + continue; + } + // Heredoc/Nowdoc: <<