diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-06 11:12:39 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-06 11:13:26 +0900 |
| commit | 9f6f83c479985e882dbd086a8495bcd772c80b54 (patch) | |
| tree | f5287fb3af16e9a04b8abc0010fb66e25dbe1937 /crates | |
| parent | 8c5dba294fae26c8a46a308a46676e0afff217d6 (diff) | |
| download | php-shirabe-9f6f83c479985e882dbd086a8495bcd772c80b54.tar.gz php-shirabe-9f6f83c479985e882dbd086a8495bcd772c80b54.tar.zst php-shirabe-9f6f83c479985e882dbd086a8495bcd772c80b54.zip | |
fix(array-merge): route mixed-key merges through faithful array_merge
* Config::merge called array_merge_recursive where PHP uses plain
array_merge (string-key overwrite); switch those six sites to
array_merge.
* provides/replaces merges that may carry an AliasPackage's
self.version numeric keys ("0","1",...) were collapsing under naive
chain/insert/or_insert; route them through a new array_merge_map().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/shirabe-php-shim/src/lib.rs | 27 | ||||
| -rw-r--r-- | crates/shirabe/src/autoload/autoload_generator.rs | 15 | ||||
| -rw-r--r-- | crates/shirabe/src/command/check_platform_reqs_command.rs | 43 | ||||
| -rw-r--r-- | crates/shirabe/src/config.rs | 20 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/installed_repository.rs | 21 |
5 files changed, 69 insertions, 57 deletions
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs index 77e51ac..acc81d9 100644 --- a/crates/shirabe-php-shim/src/lib.rs +++ b/crates/shirabe-php-shim/src/lib.rs @@ -1079,10 +1079,37 @@ pub fn array_fill_keys(_keys: PhpMixed, _value: PhpMixed) -> PhpMixed { todo!() } +/// PHP `array_merge`. +/// +/// Must reproduce PHP's mixed integer/string key semantics: +/// - string keys: a later array's value overwrites an earlier one, keeping the +/// earlier key's position; +/// - integer-like keys ("0","1",...): values are appended and renumbered +/// sequentially across all inputs (they are NOT overwritten by key). +/// +/// A naive per-entry `IndexMap::insert` is INCORRECT for inputs that mix string +/// and integer keys (e.g. an AliasPackage's provides/replaces, where +/// self.version expansion appends links under "0","1",... keys). See the typed +/// [`array_merge_map`] variant used by such call sites. pub fn array_merge(_array1: PhpMixed, _array2: PhpMixed) -> PhpMixed { todo!() } +/// PHP `array_merge` for a string-keyed map that MAY also contain integer-like +/// keys. Typed counterpart of [`array_merge`] for `IndexMap<String, V>` values +/// (e.g. `Link` maps from `getProvides`/`getReplaces`). +/// +/// Must reproduce the same mixed-key semantics as [`array_merge`]: string keys +/// overwrite in place (later wins), integer-like keys ("0","1",...) are appended +/// and renumbered sequentially across both inputs. A naive `IndexMap::insert` +/// per entry is INCORRECT because it would collide on shared integer keys. +pub fn array_merge_map<V>( + _array1: IndexMap<String, V>, + _array2: IndexMap<String, V>, +) -> IndexMap<String, V> { + todo!() +} + pub fn substr_replace(_string: &str, _replace: &str, _start: usize, _length: usize) -> String { todo!() } diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index c36125f..6505510 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -8,11 +8,11 @@ use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_external_packages::symfony::component::console::formatter::OutputFormatter; use shirabe_php_shim::{ E_USER_DEPRECATED, InvalidArgumentException, PhpMixed, RuntimeException, array_filter, - array_keys, array_map, array_merge, array_merge_recursive, array_reverse, array_shift, - array_slice, array_slice_strs, array_unique, bin2hex, explode, file_exists, file_get_contents, - hash, implode, in_array, is_array, krsort, ksort, ltrim, preg_quote, random_bytes, realpath, - sprintf, str_contains, str_replace, str_starts_with, strlen, strpos, strtr, substr, - substr_count, trigger_error, trim, unlink, var_export, + array_keys, array_map, array_merge, array_merge_map, array_merge_recursive, array_reverse, + array_shift, array_slice, array_slice_strs, array_unique, bin2hex, explode, file_exists, + file_get_contents, hash, implode, in_array, is_array, krsort, ksort, ltrim, preg_quote, + random_bytes, realpath, sprintf, str_contains, str_replace, str_starts_with, strlen, strpos, + strtr, substr, substr_count, trigger_error, trim, unlink, var_export, }; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::Bound; @@ -1104,10 +1104,7 @@ impl AutoloadGenerator { for item in package_map { let package = &item.0; - let mut links = package.get_replaces(); - for (k, v) in package.get_provides() { - links.insert(k, v); - } + let links = array_merge_map(package.get_replaces(), package.get_provides()); for (_k, link) in &links { let mut matches: IndexMap<CaptureKey, String> = IndexMap::new(); if Preg::match3("{^ext-(.+)$}iD", link.get_target(), Some(&mut matches)) diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index 14b3be2..5225f7e 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -4,7 +4,7 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::symfony::component::console::input::InputInterface; use shirabe_external_packages::symfony::component::console::output::OutputInterface; -use shirabe_php_shim::{PhpMixed, strip_tags}; +use shirabe_php_shim::{PhpMixed, array_merge_map, strip_tags}; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::SimpleConstraint; @@ -153,28 +153,27 @@ impl CheckPlatformReqsCommand { if !candidates.is_empty() { let mut req_results: Vec<CheckResult> = vec![]; 'candidates: for candidate in &candidates { - let candidate_constraint: Option<AnyConstraint> = - if candidate.get_name() == *require { - let c = SimpleConstraint::new( - "=".to_string(), - candidate.get_version().to_string(), - Some(candidate.get_pretty_version().to_string()), - ); - Some(c.into()) - } else { - let mut found: Option<AnyConstraint> = None; - for (_, link) in candidate - .get_provides() - .iter() - .chain(candidate.get_replaces().iter()) - { - if link.get_target() == require { - found = Some(link.get_constraint().clone()); - break; - } + let candidate_constraint: Option<AnyConstraint> = if candidate.get_name() + == *require + { + let c = SimpleConstraint::new( + "=".to_string(), + candidate.get_version().to_string(), + Some(candidate.get_pretty_version().to_string()), + ); + Some(c.into()) + } else { + let mut found: Option<AnyConstraint> = None; + let provides_and_replaces = + array_merge_map(candidate.get_provides(), candidate.get_replaces()); + for (_, link) in &provides_and_replaces { + if link.get_target() == require { + found = Some(link.get_constraint().clone()); + break; } - found - }; + } + found + }; let candidate_constraint = match candidate_constraint { Some(c) => c, diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs index b3460e8..1cfb8cc 100644 --- a/crates/shirabe/src/config.rs +++ b/crates/shirabe/src/config.rs @@ -12,7 +12,7 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ E_USER_DEPRECATED, FILTER_VALIDATE_URL, PHP_URL_HOST, PHP_URL_SCHEME, PhpMixed, - RuntimeException, array_key_exists, array_merge_recursive, array_reverse, array_search_mixed, + RuntimeException, array_key_exists, array_merge, array_reverse, array_search_mixed, array_unique, current, empty, filter_var, implode, in_array, is_array, is_int, is_string, key, max, parse_url, reset, rtrim, strtolower, strtoupper, strtr, substr, trigger_error, }; @@ -334,10 +334,8 @@ impl Config { ) && self.config.contains_key(key) { let existing = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); - self.config.insert( - key.clone(), - array_merge_recursive(vec![existing, val.clone()]), - ); + self.config + .insert(key.clone(), array_merge(existing, val.clone())); self.set_source_of_config_value(&val, key, source); } else if in_array( PhpMixed::String(key.clone()), @@ -354,7 +352,7 @@ impl Config { let existing = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); self.config.insert( key.clone(), - array_merge_recursive(vec![val.clone(), existing, val.clone()]), + array_merge(array_merge(val.clone(), existing), val.clone()), ); self.set_source_of_config_value(&val, key, source); } else if in_array( @@ -367,7 +365,7 @@ impl Config { ) && self.config.contains_key(key) { let existing = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); - let merged = array_merge_recursive(vec![existing, val.clone()]); + let merged = array_merge(existing, val.clone()); let unique_list: Vec<String> = match &merged { PhpMixed::List(l) => l .iter() @@ -406,7 +404,7 @@ impl Config { } let cur = self.config.get(key).cloned().unwrap_or(PhpMixed::Null); self.config - .insert(key.clone(), array_merge_recursive(vec![cur, val.clone()])); + .insert(key.clone(), array_merge(cur, val.clone())); self.set_source_of_config_value(&val, key, source); // the full match pattern needs to be last let has_wildcard = matches!( @@ -433,10 +431,10 @@ impl Config { .cloned() .map(|b| *b) .unwrap_or(PhpMixed::List(vec![])); - let merged = array_merge_recursive(vec![ + let merged = array_merge( self.config.get("audit").cloned().unwrap_or(PhpMixed::Null), val.clone(), - ]); + ); self.config.insert(key.clone(), merged); self.set_source_of_config_value(&val, key, source); let val_ignore = match &val { @@ -447,7 +445,7 @@ impl Config { .unwrap_or(PhpMixed::List(vec![])), _ => PhpMixed::List(vec![]), }; - let new_ignores = array_merge_recursive(vec![current_ignores, val_ignore]); + let new_ignores = array_merge(current_ignores, val_ignore); if let Some(PhpMixed::Array(audit)) = self.config.get_mut("audit") { audit.insert("ignore".to_string(), Box::new(new_ignores)); } diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs index e199556..e6f022e 100644 --- a/crates/shirabe/src/repository/installed_repository.rs +++ b/crates/shirabe/src/repository/installed_repository.rs @@ -2,6 +2,7 @@ use indexmap::IndexMap; use shirabe_php_shim::LogicException; +use shirabe_php_shim::array_merge_map; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use shirabe_semver::constraint::SimpleConstraint; @@ -84,16 +85,9 @@ impl InstalledRepository { continue; } - let provides = candidate.get_provides(); - let replaces = candidate.get_replaces(); - let mut provides_and_replaces: Vec<&Link> = vec![]; - for link in provides.values() { - provides_and_replaces.push(link); - } - for link in replaces.values() { - provides_and_replaces.push(link); - } - for link in provides_and_replaces { + let provides_and_replaces = + array_merge_map(candidate.get_provides(), candidate.get_replaces()); + for link in provides_and_replaces.values() { if name == link.get_target() && (constraint.is_none() || constraint.as_ref().unwrap().matches(link.get_constraint())) @@ -313,11 +307,8 @@ impl InstalledRepository { .into(); if link.get_target() != pkg.get_name().as_str() { - let mut replaces_and_provides: IndexMap<String, Link> = - pkg.get_replaces(); - for (k, v) in pkg.get_provides() { - replaces_and_provides.entry(k).or_insert(v); - } + let replaces_and_provides = + array_merge_map(pkg.get_replaces(), pkg.get_provides()); for prov in replaces_and_provides.values() { if link.get_target() == prov.get_target() { version = prov.get_constraint().clone(); |
