diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-17 03:23:52 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-17 03:23:52 +0900 |
| commit | abffae07ed350ebb1de98edd2ae246ccddfa6085 (patch) | |
| tree | 6c917cc08e39ad95078505951bdea4b9ae538630 /crates/shirabe-php-shim/src/preg.rs | |
| parent | 8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324 (diff) | |
| download | php-shirabe-abffae07ed350ebb1de98edd2ae246ccddfa6085.tar.gz php-shirabe-abffae07ed350ebb1de98edd2ae246ccddfa6085.tar.zst php-shirabe-abffae07ed350ebb1de98edd2ae246ccddfa6085.zip | |
refactor(preg): merge preg_match_all_offset_capture2() into its sibling
The two functions ran the same search and differed only in how they
reported a non-participating group: one as ("", 0) in a bespoke struct,
the other as (null, -1) in a capture-key map. Neither shape covered both
callers, because PHP reaches preg_match_all() with different flags from
each: Preg::matchAllWithOffsets() always ORs in PREG_UNMATCHED_AS_NULL,
while OutputFormatter::formatAndWrap() passes PREG_OFFSET_CAPTURE alone.
Keep the capture-key map, which also exposes named groups, and take the
flags that decide between null and "" for an unmatched group. The offset
is -1 either way, so the ("", 0) approximation is gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim/src/preg.rs')
| -rw-r--r-- | crates/shirabe-php-shim/src/preg.rs | 46 |
1 files changed, 6 insertions, 40 deletions
diff --git a/crates/shirabe-php-shim/src/preg.rs b/crates/shirabe-php-shim/src/preg.rs index 40eb25fb..c762cb95 100644 --- a/crates/shirabe-php-shim/src/preg.rs +++ b/crates/shirabe-php-shim/src/preg.rs @@ -15,17 +15,6 @@ pub enum CaptureKey { ByName(String), } -#[derive(Debug, Default)] -pub struct PregOffsetCaptureMatches { - groups: Vec<Vec<(String, usize)>>, -} - -impl PregOffsetCaptureMatches { - pub fn group(&self, i: usize) -> &[(String, usize)] { - &self.groups[i] - } -} - pub fn preg_quote(str: &str, delimiter: Option<char>) -> String { // Regex pattern compatibility: // PHP's preg_quote escapes `<` and `>` (PCRE treats `\<`/`\>` as literals), but the `regex` @@ -171,38 +160,12 @@ pub fn preg_match_all_set_order( pub fn preg_match_all_offset_capture( pattern: impl PregPattern, subject: &str, - matches: &mut PregOffsetCaptureMatches, -) -> usize { - let __resolved = pattern.resolve(); - let (re, _anchored) = __resolved.parts(); - let group_count = re.captures_len(); - matches.groups = vec![Vec::new(); group_count]; - - let mut count = 0; - for caps in re.captures_iter(subject) { - count += 1; - for g in 0..group_count { - // PHP stores ["", -1] for non-participating groups under - // PREG_OFFSET_CAPTURE; the unsigned offset here approximates -1 as 0, - // which callers must not rely on for absent groups. - let entry = caps - .get(g) - .map(|m| (m.as_str().to_string(), m.start())) - .unwrap_or_else(|| (String::new(), 0)); - matches.groups[g].push(entry); - } - } - - count -} - -pub fn preg_match_all_offset_capture2( - pattern: impl PregPattern, - subject: &str, matches: &mut indexmap::IndexMap<CaptureKey, Vec<(Option<String>, i64)>>, + flags: i64, ) -> usize { let __resolved = pattern.resolve(); let (re, _anchored) = __resolved.parts(); + let unmatched_as_null = flags & PREG_UNMATCHED_AS_NULL != 0; let group_count = re.captures_len(); let names: Vec<Option<&str>> = re.capture_names().collect(); @@ -211,9 +174,12 @@ pub fn preg_match_all_offset_capture2( for caps in re.captures_iter(subject) { count += 1; for (g, column) in groups.iter_mut().enumerate() { + // A non-participating group is reported at offset -1, holding "" or, + // with PREG_UNMATCHED_AS_NULL, null. let entry = match caps.get(g) { Some(m) => (Some(m.as_str().to_string()), m.start() as i64), - None => (None, -1), + None if unmatched_as_null => (None, -1), + None => (Some(String::new()), -1), }; column.push(entry); } |
