aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/autoload/autoload_generator.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-27 08:44:57 +0900
committernsfisis <nsfisis@gmail.com>2026-06-27 08:44:57 +0900
commit901878ee3f2bee6605b02d321cc4c92bc32fd5b0 (patch)
tree83d8c8c9a24cb95c0656866d328b87c2d4c5127f /crates/shirabe/src/autoload/autoload_generator.rs
parent5c2c72223cb6b4d77a332eeeeff7ee4e82e3f239 (diff)
downloadphp-shirabe-901878ee3f2bee6605b02d321cc4c92bc32fd5b0.tar.gz
php-shirabe-901878ee3f2bee6605b02d321cc4c92bc32fd5b0.tar.zst
php-shirabe-901878ee3f2bee6605b02d321cc4c92bc32fd5b0.zip
refactor(composer): hold managers behind *Interface traits
Composer/PartialComposer exposed its RepositoryManager, InstallationManager, EventDispatcher, Locker, DownloadManager, AutoloadGenerator and ArchiveManager as concrete types, but Composer's public setters (setDownloadManager() etc.) let plugins swap in subclasses. Introduce a *Interface trait per manager and store each as Rc<RefCell<dyn ...Interface>> so a replacement is honored. Only Composer's slots and the sinks fed from its accessors become trait objects; managers injected concretely at construction keep their concrete references, matching PHP semantics. Fluent setters on the affected classes now return () and Locker::update_hash is de-generified to a boxed FnOnce so the traits stay object-safe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/autoload/autoload_generator.rs')
-rw-r--r--crates/shirabe/src/autoload/autoload_generator.rs136
1 files changed, 131 insertions, 5 deletions
diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs
index 1bdea07..1d0960d 100644
--- a/crates/shirabe/src/autoload/autoload_generator.rs
+++ b/crates/shirabe/src/autoload/autoload_generator.rs
@@ -21,12 +21,12 @@ use crate::event_dispatcher::EventDispatcher;
use crate::filter::platform_requirement_filter::IgnoreAllPlatformRequirementFilter;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterFactory;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
-use crate::installer::InstallationManager;
+use crate::installer::InstallationManagerInterface;
use crate::io::IOInterface;
use crate::io::IOInterfaceImmutable;
use crate::io::NullIO;
use crate::json::JsonFile;
-use crate::package::Locker;
+use crate::package::LockerInterface;
use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterfaceHandle;
use crate::repository::InstalledRepositoryInterface;
@@ -107,11 +107,11 @@ impl AutoloadGenerator {
config: &Config,
local_repo: &mut dyn InstalledRepositoryInterface,
root_package: RootPackageInterfaceHandle,
- installation_manager: &mut InstallationManager,
+ installation_manager: &mut dyn InstallationManagerInterface,
target_dir: &str,
scan_psr_packages: bool,
suffix: Option<String>,
- locker: Option<&mut Locker>,
+ locker: Option<&mut dyn LockerInterface>,
strict_ambiguous: bool,
) -> anyhow::Result<ClassMap> {
let mut scan_psr_packages = scan_psr_packages;
@@ -732,7 +732,7 @@ impl AutoloadGenerator {
pub fn build_package_map(
&self,
- installation_manager: &mut InstallationManager,
+ installation_manager: &mut dyn InstallationManagerInterface,
root_package: RootPackageInterfaceHandle,
packages: Vec<PackageInterfaceHandle>,
) -> anyhow::Result<Vec<(PackageInterfaceHandle, Option<String>)>> {
@@ -1930,6 +1930,132 @@ impl AutoloadGenerator {
}
}
+// Composer's Composer::setAutoloadGenerator() accepts any AutoloadGenerator subclass, so plugins
+// may swap in a replacement. The interface captures the methods reached through Composer's accessor
+// and through the `Rc<RefCell<dyn AutoloadGeneratorInterface>>` references fed from it.
+pub trait AutoloadGeneratorInterface: std::fmt::Debug {
+ fn set_dev_mode(&mut self, dev_mode: bool);
+ fn set_class_map_authoritative(&mut self, class_map_authoritative: bool);
+ fn set_apcu(&mut self, apcu: bool, apcu_prefix: Option<String>);
+ fn set_run_scripts(&mut self, run_scripts: bool);
+ fn set_dry_run(&mut self, dry_run: bool);
+ fn set_platform_requirement_filter(
+ &mut self,
+ platform_requirement_filter: std::rc::Rc<dyn PlatformRequirementFilterInterface>,
+ );
+ #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")]
+ fn dump(
+ &mut self,
+ config: &Config,
+ local_repo: &mut dyn InstalledRepositoryInterface,
+ root_package: RootPackageInterfaceHandle,
+ installation_manager: &mut dyn InstallationManagerInterface,
+ target_dir: &str,
+ scan_psr_packages: bool,
+ suffix: Option<String>,
+ locker: Option<&mut dyn LockerInterface>,
+ strict_ambiguous: bool,
+ ) -> anyhow::Result<ClassMap>;
+ fn build_package_map(
+ &self,
+ installation_manager: &mut dyn InstallationManagerInterface,
+ root_package: RootPackageInterfaceHandle,
+ packages: Vec<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Vec<(PackageInterfaceHandle, Option<String>)>>;
+ fn parse_autoloads(
+ &self,
+ package_map: Vec<(PackageInterfaceHandle, Option<String>)>,
+ root_package: RootPackageInterfaceHandle,
+ filtered_dev_packages: PhpMixed,
+ ) -> IndexMap<String, PhpMixed>;
+ fn create_loader(
+ &self,
+ autoloads: &IndexMap<String, PhpMixed>,
+ vendor_dir: Option<String>,
+ ) -> ClassLoader;
+}
+
+impl AutoloadGeneratorInterface for AutoloadGenerator {
+ fn set_dev_mode(&mut self, dev_mode: bool) {
+ self.set_dev_mode(dev_mode);
+ }
+
+ fn set_class_map_authoritative(&mut self, class_map_authoritative: bool) {
+ self.set_class_map_authoritative(class_map_authoritative);
+ }
+
+ fn set_apcu(&mut self, apcu: bool, apcu_prefix: Option<String>) {
+ self.set_apcu(apcu, apcu_prefix);
+ }
+
+ fn set_run_scripts(&mut self, run_scripts: bool) {
+ self.set_run_scripts(run_scripts);
+ }
+
+ fn set_dry_run(&mut self, dry_run: bool) {
+ self.set_dry_run(dry_run);
+ }
+
+ fn set_platform_requirement_filter(
+ &mut self,
+ platform_requirement_filter: std::rc::Rc<dyn PlatformRequirementFilterInterface>,
+ ) {
+ self.set_platform_requirement_filter(platform_requirement_filter);
+ }
+
+ #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")]
+ fn dump(
+ &mut self,
+ config: &Config,
+ local_repo: &mut dyn InstalledRepositoryInterface,
+ root_package: RootPackageInterfaceHandle,
+ installation_manager: &mut dyn InstallationManagerInterface,
+ target_dir: &str,
+ scan_psr_packages: bool,
+ suffix: Option<String>,
+ locker: Option<&mut dyn LockerInterface>,
+ strict_ambiguous: bool,
+ ) -> anyhow::Result<ClassMap> {
+ self.dump(
+ config,
+ local_repo,
+ root_package,
+ installation_manager,
+ target_dir,
+ scan_psr_packages,
+ suffix,
+ locker,
+ strict_ambiguous,
+ )
+ }
+
+ fn build_package_map(
+ &self,
+ installation_manager: &mut dyn InstallationManagerInterface,
+ root_package: RootPackageInterfaceHandle,
+ packages: Vec<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Vec<(PackageInterfaceHandle, Option<String>)>> {
+ self.build_package_map(installation_manager, root_package, packages)
+ }
+
+ fn parse_autoloads(
+ &self,
+ package_map: Vec<(PackageInterfaceHandle, Option<String>)>,
+ root_package: RootPackageInterfaceHandle,
+ filtered_dev_packages: PhpMixed,
+ ) -> IndexMap<String, PhpMixed> {
+ self.parse_autoloads(package_map, root_package, filtered_dev_packages)
+ }
+
+ fn create_loader(
+ &self,
+ autoloads: &IndexMap<String, PhpMixed>,
+ vendor_dir: Option<String>,
+ ) -> ClassLoader {
+ self.create_loader(autoloads, vendor_dir)
+ }
+}
+
/// Evaluates one of the `autoload_namespaces.php` / `autoload_psr4.php` / `autoload_classmap.php`
/// files that `dump` just generated, returning the `return array(...)` payload with every path
/// expression resolved to an absolute string. This stands in for PHP's `require $file` (there is no