From 8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 17 Aug 2026 03:09:04 +0900 Subject: fix(pcre): preserve unmatched groups in Preg::match_all*() PHP's Preg::matchAll() and matchAllWithOffsets() always set PREG_UNMATCHED_AS_NULL, so a non-participating group is `null` and its offset is -1. The Rust wrappers collapsed those to "" and 0, so callers could not tell a group that did not participate from one that matched an empty string at offset 0, and the offset value matched no PHP mode at all. Hand the shim's representation through unchanged and let each caller mirror what the PHP original does with it: `isset()` and `(string)` casts stay lenient, while `assert(is_string(...))` and the *StrictGroups() variants become `expect()`. Co-Authored-By: Claude Opus 5 (1M context) --- .../shirabe-class-map-generator/src/php_file_parser.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'crates/shirabe-class-map-generator') diff --git a/crates/shirabe-class-map-generator/src/php_file_parser.rs b/crates/shirabe-class-map-generator/src/php_file_parser.rs index 0c82a558..e6eeb028 100644 --- a/crates/shirabe-class-map-generator/src/php_file_parser.rs +++ b/crates/shirabe-class-map-generator/src/php_file_parser.rs @@ -99,13 +99,12 @@ impl PhpFileParser { let ns = matches .get(&CaptureKey::ByName("ns".to_owned())) .and_then(|v| v.get(i)) - .map(|s| s.as_str()) - .unwrap_or(""); - if !ns.is_empty() { + .and_then(|s| s.as_deref()); + if ns.is_some_and(|ns| !ns.is_empty()) { let nsname = matches .get(&CaptureKey::ByName("nsname".to_owned())) .and_then(|v| v.get(i)) - .map(|s| s.as_str()) + .and_then(|s| s.as_deref()) .unwrap_or(""); namespace = str_replace_array( &[ @@ -121,8 +120,8 @@ impl PhpFileParser { let name = matches .get(&CaptureKey::ByName("name".to_owned())) .and_then(|v| v.get(i)) - .map(|s| s.as_str()) - .unwrap_or(""); + .and_then(|s| s.as_deref()) + .expect("the `name` group participates whenever `ns` does not"); // skip anon classes extending/implementing if name == "extends" { continue; @@ -142,9 +141,10 @@ impl PhpFileParser { } else if matches .get(&CaptureKey::ByName("type".to_owned())) .and_then(|v| v.get(i)) - .map(|s| s.to_lowercase()) - .as_deref() - == Some("enum") + .and_then(|s| s.as_deref()) + .unwrap_or("") + .to_lowercase() + == "enum" { // something like: // enum Foo: int { HERP = '123'; } -- cgit v1.3.1-4-g156e