From 318ea948f5932dfa7942081a269d62fd7161a9bf Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 8 Jun 2026 01:54:56 +0900 Subject: feat(phase-c): resolve reflection/downcast phase-b TODOs Resolve category F phase-b TODOs (class-string, instanceof, get_class, method_exists, __FILE__, Reflection API, downcast). - VcsRepository: dispatch drivers through a VcsDriverKind enum (instantiate/supports/php_class_name) and add constructors to the concrete VCS drivers - repository downcasts via RepositoryInterfaceHandle::downcast_rc and as_any (init/show commands, vcs ValidatingArrayLoader) - BaseCommand::is_self_update_command override replaces an instanceof - Factory::create narrows PartialComposer to ComposerHandle via as_full - InstalledVersions gains set_self_dir/set_installed_is_local_dir, replacing Reflection-based static property mutation - ClassLoader::as_array_iter ports the PHP (array) cast - drop the unnecessary __FILE__ phar branch in self-update application get_class(command) reclassified TODO(plugin); buffer_io StreamableInputInterface downcast and the ValidatingArrayLoader trait redesign left as tracked TODOs. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/autoload/class_loader.rs | 102 +++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) (limited to 'crates/shirabe/src/autoload/class_loader.rs') diff --git a/crates/shirabe/src/autoload/class_loader.rs b/crates/shirabe/src/autoload/class_loader.rs index edb1868..99ee5c2 100644 --- a/crates/shirabe/src/autoload/class_loader.rs +++ b/crates/shirabe/src/autoload/class_loader.rs @@ -530,8 +530,106 @@ impl ClassLoader { // Rust has no `include` operator; this is a no-op placeholder. } + /// PHP `(array) $loader`. Every property is private, so keys are mangled as + /// `"\0Composer\Autoload\ClassLoader\0"` using the original camelCase names, in + /// declaration order. pub fn as_array_iter(&self) -> Vec<(String, PhpMixed)> { - // TODO(phase-b): iterate over loader properties as PHP (array) cast would - todo!() + let key = |name: &str| format!("\0Composer\\Autoload\\ClassLoader\0{}", name); + let str_list = |v: &Vec| { + PhpMixed::List( + v.iter() + .map(|s| Box::new(PhpMixed::String(s.clone()))) + .collect(), + ) + }; + + vec![ + ( + key("vendorDir"), + match &self.vendor_dir { + Some(s) => PhpMixed::String(s.clone()), + None => PhpMixed::Null, + }, + ), + ( + key("prefixLengthsPsr4"), + PhpMixed::Array( + self.prefix_lengths_psr4 + .iter() + .map(|(k, inner)| { + ( + k.clone(), + Box::new(PhpMixed::Array( + inner + .iter() + .map(|(k2, n)| (k2.clone(), Box::new(PhpMixed::Int(*n)))) + .collect(), + )), + ) + }) + .collect(), + ), + ), + ( + key("prefixDirsPsr4"), + PhpMixed::Array( + self.prefix_dirs_psr4 + .iter() + .map(|(k, v)| (k.clone(), Box::new(str_list(v)))) + .collect(), + ), + ), + (key("fallbackDirsPsr4"), str_list(&self.fallback_dirs_psr4)), + ( + key("prefixesPsr0"), + PhpMixed::Array( + self.prefixes_psr0 + .iter() + .map(|(k, inner)| { + ( + k.clone(), + Box::new(PhpMixed::Array( + inner + .iter() + .map(|(k2, v)| (k2.clone(), Box::new(str_list(v)))) + .collect(), + )), + ) + }) + .collect(), + ), + ), + (key("fallbackDirsPsr0"), str_list(&self.fallback_dirs_psr0)), + (key("useIncludePath"), PhpMixed::Bool(self.use_include_path)), + ( + key("classMap"), + PhpMixed::Array( + self.class_map + .iter() + .map(|(k, v)| (k.clone(), Box::new(PhpMixed::String(v.clone())))) + .collect(), + ), + ), + ( + key("classMapAuthoritative"), + PhpMixed::Bool(self.class_map_authoritative), + ), + ( + key("missingClasses"), + PhpMixed::Array( + self.missing_classes + .iter() + .map(|(k, b)| (k.clone(), Box::new(PhpMixed::Bool(*b)))) + .collect(), + ), + ), + ( + key("apcuPrefix"), + match &self.apcu_prefix { + Some(s) => PhpMixed::String(s.clone()), + None => PhpMixed::Null, + }, + ), + ] } } -- cgit v1.3.1