From ac544bc3e1cb90cdcce4636654d0e266a78a2f85 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 20 Jun 2026 14:57:38 +0900 Subject: feat(php-shim): implement array_replace_recursive Was a todo!() that panicked while building HTTP downloader options (browse, and the stream/remote filesystem option merging). Recurses only when both the existing and replacing value are arrays, merging assoc arrays by key and lists by index; otherwise the replacing value wins. Verified against PHP on a nested http/ssl option structure. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe-php-shim/src/lib.rs | 63 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) (limited to 'crates/shirabe-php-shim') diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs index c30c434..4f7b4d2 100644 --- a/crates/shirabe-php-shim/src/lib.rs +++ b/crates/shirabe-php-shim/src/lib.rs @@ -1900,10 +1900,67 @@ where } pub fn array_replace_recursive( - _base: IndexMap, - _replacement: IndexMap, + mut base: IndexMap, + replacement: IndexMap, ) -> IndexMap { - todo!() + for (key, replacement_value) in replacement { + let merged = match base.get(&key) { + Some(base_value) => { + array_replace_recursive_value(base_value.clone(), replacement_value) + } + None => replacement_value, + }; + base.insert(key, merged); + } + base +} + +// PHP recurses only when both the existing and the replacing value are arrays; +// otherwise the replacing value wins outright. +fn array_replace_recursive_value(base: PhpMixed, replacement: PhpMixed) -> PhpMixed { + match (base, replacement) { + (PhpMixed::Array(base), PhpMixed::Array(replacement)) => { + PhpMixed::Array(array_replace_recursive_assoc(base, replacement)) + } + (PhpMixed::List(base), PhpMixed::List(replacement)) => { + PhpMixed::List(array_replace_recursive_list(base, replacement)) + } + (_, replacement) => replacement, + } +} + +fn array_replace_recursive_assoc( + mut base: IndexMap>, + replacement: IndexMap>, +) -> IndexMap> { + for (key, replacement_value) in replacement { + let merged = match base.get(&key) { + Some(base_value) => Box::new(array_replace_recursive_value( + (**base_value).clone(), + *replacement_value, + )), + None => replacement_value, + }; + base.insert(key, merged); + } + base +} + +fn array_replace_recursive_list( + mut base: Vec>, + replacement: Vec>, +) -> Vec> { + for (index, replacement_value) in replacement.into_iter().enumerate() { + if index < base.len() { + base[index] = Box::new(array_replace_recursive_value( + (*base[index]).clone(), + *replacement_value, + )); + } else { + base.push(replacement_value); + } + } + base } pub const PHP_MAJOR_VERSION: i64 = 8; -- cgit v1.3.1