From 20dbcf11b86cb03c451ba1d5cd9efe17b68fa66d Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 31 May 2026 21:34:47 +0900 Subject: fix(package): port every PHP clone operator to handle dup() --- crates/shirabe/src/package/alias_package.rs | 2 +- .../shirabe/src/package/complete_alias_package.rs | 4 +-- crates/shirabe/src/package/complete_package.rs | 2 +- crates/shirabe/src/package/handle.rs | 41 +++++++++++++++++++++- .../src/package/loader/root_package_loader.rs | 19 ++++------ crates/shirabe/src/package/locker.rs | 3 +- crates/shirabe/src/package/package.rs | 2 +- crates/shirabe/src/package/root_alias_package.rs | 14 +++++--- crates/shirabe/src/package/root_package.rs | 2 +- 9 files changed, 64 insertions(+), 25 deletions(-) (limited to 'crates/shirabe/src/package') diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index ff846a6..b4ddc70 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -13,7 +13,7 @@ use crate::package::PackageInterface; use crate::package::version::VersionParser; use crate::repository::RepositoryInterfaceHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AliasPackage { id: i64, name: String, diff --git a/crates/shirabe/src/package/complete_alias_package.rs b/crates/shirabe/src/package/complete_alias_package.rs index 6a23bee..7720258 100644 --- a/crates/shirabe/src/package/complete_alias_package.rs +++ b/crates/shirabe/src/package/complete_alias_package.rs @@ -9,9 +9,9 @@ use crate::package::CompletePackageInterface; use crate::package::PackageHandle; use crate::package::handle::delegate_package_interface_to_inner; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CompleteAliasPackage { - inner: AliasPackage, + pub(crate) inner: AliasPackage, // overrides AliasPackage::alias_of with the more specific CompletePackage type pub(crate) alias_of: CompletePackageHandle, } diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs index 55c7654..97669d8 100644 --- a/crates/shirabe/src/package/complete_package.rs +++ b/crates/shirabe/src/package/complete_package.rs @@ -6,7 +6,7 @@ use crate::package::PackageInterface; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CompletePackage { pub(crate) inner: Package, pub(crate) repositories: Vec>, diff --git a/crates/shirabe/src/package/handle.rs b/crates/shirabe/src/package/handle.rs index 746e2ce..aecc219 100644 --- a/crates/shirabe/src/package/handle.rs +++ b/crates/shirabe/src/package/handle.rs @@ -12,7 +12,7 @@ use crate::package::{ }; /// Any package type. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum AnyPackage { Package(Package), CompletePackage(CompletePackage), @@ -137,6 +137,36 @@ impl AnyPackage { pub fn is_root_alias(&self) -> bool { matches!(self, Self::RootAliasPackage(_)) } + + /// PHP `clone $package`: fresh object identity. Matches PHP's shallow + /// clone for most types (scalars/arrays are copied, nested object + /// references — including `aliasOf` on alias variants — are shared), + /// except for RootAliasPackage where PHP's `__clone` hook explicitly + /// reseats `aliasOf` to a fresh clone. + pub fn dup(&self) -> Self { + match self { + Self::Package(p) => Self::Package(p.clone()), + Self::CompletePackage(p) => Self::CompletePackage(p.clone()), + Self::RootPackage(p) => Self::RootPackage(p.clone()), + Self::AliasPackage(p) => Self::AliasPackage(p.clone()), + Self::CompleteAliasPackage(p) => Self::CompleteAliasPackage(p.clone()), + Self::RootAliasPackage(p) => { + // PHP's RootAliasPackage overrides `__clone()`: + // $this->aliasOf = clone $this->aliasOf; + let new_alias_of_inner = p.alias_of.0.borrow().dup(); + let new_alias_of_rc = Rc::new(RefCell::new(new_alias_of_inner)); + let new_root = RootPackageHandle(new_alias_of_rc.clone()); + let new_complete = CompletePackageHandle(new_alias_of_rc.clone()); + let new_pkg = PackageHandle(new_alias_of_rc); + + let mut cloned = p.clone(); + cloned.alias_of = new_root; + cloned.inner.alias_of = new_complete; + cloned.inner.inner.alias_of = new_pkg; + Self::RootAliasPackage(cloned) + } + } + } } macro_rules! delegate_package_interface_to_inner { @@ -1119,6 +1149,15 @@ macro_rules! impl_handle_common { pub fn ptr_eq(&self, other: &Self) -> bool { std::rc::Rc::ptr_eq(&self.0, &other.0) } + + /// PHP `clone $x`: fresh object identity. See [`AnyPackage::dup`] + /// for the per-variant semantics (including the RootAliasPackage + /// `__clone` hook). + pub fn dup(other: &Self) -> Self { + Self(std::rc::Rc::new(std::cell::RefCell::new( + other.0.borrow().dup(), + ))) + } } impl PartialEq for $Handle { diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs index 1b4d155..b31525f 100644 --- a/crates/shirabe/src/package/loader/root_package_loader.rs +++ b/crates/shirabe/src/package/loader/root_package_loader.rs @@ -11,8 +11,6 @@ use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; use crate::package::CompletePackageInterface; use crate::package::PackageInterface; -use crate::package::RootAliasPackage; -use crate::package::RootPackage; use crate::package::RootPackageInterface; use crate::package::loader::ArrayLoader; use crate::package::loader::LoaderInterface; @@ -194,19 +192,16 @@ impl RootPackageLoader { Some("Composer\\Package\\RootPackage".to_string()), )?; - // TODO(phase-c): mutating the loaded RootPackage through a PackageInterfaceHandle - // requires going through as_root_package() + a RefCell borrow; the inherent - // RootPackage mutators used below are not yet reachable that way. - let real_package: &mut RootPackage = { - let _ = &mut package; - todo!( - "mutate RootPackage through PackageInterfaceHandle (as_root_package + borrow_mut)" - ) + let real_package = if let Some(alias) = package.as_root_alias_package() { + alias.get_alias_of() + } else { + package + .as_root_package() + .expect("Expecting a Composer\\Package\\RootPackage at this point") }; if auto_versioned { // TODO(phase-b): replace_version is an inherent method on Package, not exposed via trait. - let _ = real_package; todo!("replace_version is not accessible through RootPackage's embedded Package"); } @@ -230,7 +225,7 @@ impl RootPackageLoader { aliases = self.extract_aliases(&links, aliases); stability_flags = Self::extract_stability_flags( &links, - real_package.get_minimum_stability(), + &real_package.get_minimum_stability(), stability_flags, ); references = Self::extract_references(&links, references); diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index 327f662..9e36255 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -951,8 +951,7 @@ impl Locker { description: "Required (in require-dev)".to_string(), }); } - // TODO(phase-b): clone $package to a RootPackageRepository - let root_repo = RootPackageRepository::new(todo!("phase-b: clone root package")); + let root_repo = RootPackageRepository::new(RootPackageInterfaceHandle::dup(&package)); for set in &sets { let installed_repo = InstalledRepository::new(vec![/* set.repo, root_repo */]); diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs index ba9e3f9..f573326 100644 --- a/crates/shirabe/src/package/package.rs +++ b/crates/shirabe/src/package/package.rs @@ -24,7 +24,7 @@ pub struct Mirror { } /// Core package definitions that are needed to resolve dependencies and install packages -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Package { id: i64, name: String, diff --git a/crates/shirabe/src/package/root_alias_package.rs b/crates/shirabe/src/package/root_alias_package.rs index 7298c1f..67e123b 100644 --- a/crates/shirabe/src/package/root_alias_package.rs +++ b/crates/shirabe/src/package/root_alias_package.rs @@ -11,9 +11,9 @@ use crate::package::RootPackageHandle; use crate::package::RootPackageInterface; use crate::package::handle::delegate_package_interface_to_inner; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RootAliasPackage { - inner: CompleteAliasPackage, + pub(crate) inner: CompleteAliasPackage, // overrides CompleteAliasPackage::alias_of with the more specific RootPackage type pub(crate) alias_of: RootPackageHandle, } @@ -83,8 +83,14 @@ impl RootPackageInterface for RootAliasPackage { } fn set_requires(&mut self, requires: Vec) { - // TODO(phase-c): PHP re-derives the local links via - // replaceSelfVersionDependencies before forwarding to aliasOf. + let replaced = self + .inner + .inner + .replace_self_version_dependencies(requires.clone(), Link::TYPE_REQUIRE); + self.inner.inner.requires = replaced + .into_iter() + .map(|l| (l.get_target().to_string(), l)) + .collect(); self.alias_of.set_requires(requires); } diff --git a/crates/shirabe/src/package/root_package.rs b/crates/shirabe/src/package/root_package.rs index 5a719de..9cedc1a 100644 --- a/crates/shirabe/src/package/root_package.rs +++ b/crates/shirabe/src/package/root_package.rs @@ -11,7 +11,7 @@ use crate::package::PackageInterface; use crate::package::RootPackageInterface; use crate::repository::RepositoryInterfaceHandle; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RootPackage { inner: CompletePackage, pub(crate) minimum_stability: String, -- cgit v1.3.1