diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-08 11:26:03 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-08 11:26:03 +0900 |
| commit | e5b789616ec4c1cbd152c5ccbefe2d27ced4a18f (patch) | |
| tree | 7a713cf1bcec30a1c69d5c434d8df6a7525dcbc2 /crates/shirabe/src/repository/filesystem_repository.rs | |
| parent | 7439cdb08afe0882186a34f70c1e8878fcb7dca5 (diff) | |
| download | php-shirabe-e5b789616ec4c1cbd152c5ccbefe2d27ced4a18f.tar.gz php-shirabe-e5b789616ec4c1cbd152c5ccbefe2d27ced4a18f.tar.zst php-shirabe-e5b789616ec4c1cbd152c5ccbefe2d27ced4a18f.zip | |
feat(phase-c): resolve PHP-array-semantics phase-b TODOs
Resolve category K (array_* functions, integer keys, nested mutation,
sorting). Add shim variants (uasort over Vec<T>, uasort_map for IndexMap)
and delegate to existing typed variants (strtr_array, array_merge_map,
array_search_in_vec). Implement PHP array semantics directly where the
shape is fixed: canonical integer-key coercion (is_php_integer_key,
shared by config and FilesystemRepository::dumpToPhpCode), strict
array_search via trait-object pointer identity, array_reverse/array_chunk
preserve_keys loops, and the installed.php nested version mutations via
auto-vivify helpers.
Resolving the array_merge in UpdateCommand unmasked latent borrow bugs in
execute's tail (Rc input/output moved by value); fixed with .clone() to
match PHP reference sharing, and resolved the tightly-coupled Intervals
constraint check.
composerRequire reclassified to phase-c: it depends on the $GLOBALS
superglobal and PHP's require include mechanism, neither portable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/repository/filesystem_repository.rs')
| -rw-r--r-- | crates/shirabe/src/repository/filesystem_repository.rs | 125 |
1 files changed, 93 insertions, 32 deletions
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs index fd7349a..4a18dc9 100644 --- a/crates/shirabe/src/repository/filesystem_repository.rs +++ b/crates/shirabe/src/repository/filesystem_repository.rs @@ -9,10 +9,11 @@ use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{ Exception, InvalidArgumentException, LogicException, PhpMixed, SORT_NATURAL, UnexpectedValueException, array_flip, dirname, r#eval, file_get_contents, get_class, - get_class_err, get_debug_type, in_array, is_array, is_int, is_null, is_string, ksort, php_dir, + get_class_err, get_debug_type, in_array, is_array, is_null, is_string, ksort, php_dir, realpath, sort, sort_with_flags, str_repeat, strtr, trim, usort, var_export, }; +use crate::config::is_php_integer_key; use crate::installed_versions::InstalledVersions; use crate::installer::InstallationManager; use crate::json::JsonFile; @@ -394,8 +395,7 @@ impl FilesystemRepository { for (key, value) in array { lines.push_str(&str_repeat(" ", level as usize)); - lines.push_str(&if is_int(&PhpMixed::String(key.clone())) { - // TODO(phase-b): PHP integer-keyed array entries — IndexMap keys are strings + lines.push_str(&if is_php_integer_key(key) { format!("{} => ", key) } else { format!("{} => ", var_export(&PhpMixed::String(key.clone()), true)) @@ -548,54 +548,56 @@ impl FilesystemRepository { if PlatformRepository::is_platform_package(replace.get_target()) { continue; } - // PHP: dev_requirement handling - // TODO(phase-b): mutate nested versions['versions'][$replace->getTarget()]['dev_requirement'] - todo!("mutate nested versions['versions'][target]['dev_requirement']"); - #[allow(unreachable_code)] - { - let mut replaced = replace.get_pretty_constraint().to_string(); - if replaced == "self.version" { - replaced = package.get_pretty_version().to_string(); - } - // TODO(phase-b): mutate nested versions['versions'][$replace->getTarget()]['replaced'] - todo!("append replaced to versions['versions'][target]['replaced']"); + let mut replaced = replace.get_pretty_constraint().to_string(); + if replaced == "self.version" { + replaced = package.get_pretty_version().to_string(); } + record_replace_or_provide( + &mut versions, + replace.get_target(), + "replaced", + replaced, + is_dev_package, + ); } for (_, provide) in package.get_provides() { // exclude platform provides as when they are really there we can not check for their presence if PlatformRepository::is_platform_package(provide.get_target()) { continue; } - // TODO(phase-b): mutate nested versions['versions'][$provide->getTarget()]['dev_requirement'] - todo!("mutate nested versions['versions'][target]['dev_requirement']"); - #[allow(unreachable_code)] - { - let mut provided = provide.get_pretty_constraint().to_string(); - if provided == "self.version" { - provided = package.get_pretty_version().to_string(); - } - // TODO(phase-b): mutate nested versions['versions'][$provide->getTarget()]['provided'] - todo!("append provided to versions['versions'][target]['provided']"); + let mut provided = provide.get_pretty_constraint().to_string(); + if provided == "self.version" { + provided = package.get_pretty_version().to_string(); } + record_replace_or_provide( + &mut versions, + provide.get_target(), + "provided", + provided, + is_dev_package, + ); } } // add aliases for package in &packages { - let Some(_alias) = package.as_alias() else { + if package.as_alias().is_none() { continue; - }; - // TODO(phase-b): mutate nested versions['versions'][name]['aliases'] - todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']"); - #[allow(unreachable_code)] + } + let pretty = package.get_pretty_version().to_string(); + push_to_list( + versions_entry(&mut versions, &package.get_name()), + "aliases", + pretty.clone(), + ); if package.as_root().is_some() { - // TODO(phase-b): same mutation on versions['root']['aliases'] - todo!("append alias->getPrettyVersion() to versions['root']['aliases']"); + if let Some(PhpMixed::Array(root_map)) = versions.get_mut("root") { + push_to_list(root_map, "aliases", pretty); + } } } if let Some(PhpMixed::Array(versions_map)) = versions.get_mut("versions") { - // TODO(phase-b): ksort signature mismatch on nested IndexMap; cast appropriately ksort(versions_map); } ksort(&mut versions); @@ -767,3 +769,62 @@ impl FilesystemRepository { result } } + +fn versions_entry<'a>( + versions: &'a mut IndexMap<String, PhpMixed>, + target: &str, +) -> &'a mut IndexMap<String, Box<PhpMixed>> { + let versions_map = match versions.get_mut("versions") { + Some(PhpMixed::Array(m)) => m, + _ => unreachable!("versions['versions'] is always an array"), + }; + match versions_map + .entry(target.to_string()) + .or_insert_with(|| Box::new(PhpMixed::Array(IndexMap::new()))) + .as_mut() + { + PhpMixed::Array(m) => m, + _ => unreachable!("versions['versions'][target] is always an array"), + } +} + +fn push_to_list(entry: &mut IndexMap<String, Box<PhpMixed>>, key: &str, value: String) { + if let PhpMixed::List(list) = entry + .entry(key.to_string()) + .or_insert_with(|| Box::new(PhpMixed::List(vec![]))) + .as_mut() + { + list.push(Box::new(PhpMixed::String(value))); + } +} + +fn record_replace_or_provide( + versions: &mut IndexMap<String, PhpMixed>, + target: &str, + key: &str, + value: String, + is_dev_package: bool, +) { + let entry = versions_entry(versions, target); + if !entry.contains_key("dev_requirement") { + entry.insert( + "dev_requirement".to_string(), + Box::new(PhpMixed::Bool(is_dev_package)), + ); + } else if !is_dev_package { + entry.insert( + "dev_requirement".to_string(), + Box::new(PhpMixed::Bool(false)), + ); + } + let already_present = match entry.get(key) { + Some(b) => matches!( + b.as_ref(), + PhpMixed::List(list) if list.iter().any(|v| v.as_string() == Some(value.as_str())) + ), + None => false, + }; + if !already_present { + push_to_list(entry, key, value); + } +} |
