diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-22 15:33:18 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-22 15:33:18 +0900 |
| commit | 3bddf91f8fe386cc9d908ed498ea0b3235790904 (patch) | |
| tree | 85e59f694609a9c36eaed059591a774023b7b49a | |
| parent | f7f1ae38b765a10cb37fb2ecc68daea3401954fd (diff) | |
| download | php-mozart-3bddf91f8fe386cc9d908ed498ea0b3235790904.tar.gz php-mozart-3bddf91f8fe386cc9d908ed498ea0b3235790904.tar.zst php-mozart-3bddf91f8fe386cc9d908ed498ea0b3235790904.zip | |
fix(platform): detect PHP version from runtime instead of hardcoding 8.1
PlatformConfig::new() was hardcoded to PHP 8.1 with a fixed extension
list, causing resolution failures for packages requiring newer PHP
(e.g. Laravel 12 requires >=8.2). Now calls detect_platform() to
discover the actual PHP version, extensions and capabilities.
Also adds a mutex to composer_home_dir tests to prevent env-var races.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| -rw-r--r-- | crates/mozart-registry/src/resolver.rs | 57 | ||||
| -rw-r--r-- | crates/mozart/src/commands/global.rs | 19 |
2 files changed, 39 insertions, 37 deletions
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs index 84e6a96..74acc52 100644 --- a/crates/mozart-registry/src/resolver.rs +++ b/crates/mozart-registry/src/resolver.rs @@ -433,33 +433,17 @@ impl Default for PlatformConfig { } impl PlatformConfig { - /// Create a default platform config with PHP 8.1 and common extensions. + /// Detect platform packages from the local PHP installation. + /// + /// Runs `php -r …` to discover the PHP version, extensions and + /// capabilities. Returns an empty config when PHP is not found. pub fn new() -> Self { + let detected = mozart_core::platform::detect_platform(); let mut packages = HashMap::new(); - packages.insert("php".to_string(), "8.1.0.0".to_string()); - packages.insert("php-64bit".to_string(), "8.1.0.0".to_string()); - for ext in &[ - "json", - "mbstring", - "openssl", - "pdo", - "tokenizer", - "xml", - "ctype", - "iconv", - "curl", - "dom", - "fileinfo", - "filter", - "hash", - "pcre", - "session", - "zlib", - "intl", - "gd", - "bcmath", - ] { - packages.insert(format!("ext-{ext}"), "8.1.0.0".to_string()); + for pkg in detected { + // Normalize version to four-component form (e.g. "8.2.1" → "8.2.1.0") + let normalized = normalize_platform_version(&pkg.version); + packages.insert(pkg.name, normalized); } Self { packages } } @@ -475,6 +459,17 @@ impl PlatformConfig { } } +/// Pad a version string to four dot-separated components (e.g. "8.2.1" → "8.2.1.0"). +fn normalize_platform_version(version: &str) -> String { + let parts: Vec<&str> = version.split('.').collect(); + match parts.len() { + 1 => format!("{}.0.0.0", parts[0]), + 2 => format!("{}.{}.0.0", parts[0], parts[1]), + 3 => format!("{}.{}.{}.0", parts[0], parts[1], parts[2]), + _ => version.to_string(), + } +} + // ───────────────────────────────────────────────────────────────────────────── // Error types // ───────────────────────────────────────────────────────────────────────────── @@ -1447,11 +1442,13 @@ mod tests { fn test_platform_config_to_versions() { let config = PlatformConfig::new(); let versions = config.to_versions(); - assert!(versions.contains_key("php")); - assert!(versions.contains_key("ext-json")); - let php_v = versions["php"]; - assert_eq!(php_v.major, 8); - assert_eq!(php_v.minor, 1); + // If PHP is available on the system, we should have detected it + if !config.packages.is_empty() { + assert!( + versions.contains_key("php"), + "detected packages should include php" + ); + } } // ──────────── Integration tests (offline, using OfflineDependencyProvider) ──────────── diff --git a/crates/mozart/src/commands/global.rs b/crates/mozart/src/commands/global.rs index a646f7a..97d56d2 100644 --- a/crates/mozart/src/commands/global.rs +++ b/crates/mozart/src/commands/global.rs @@ -121,38 +121,43 @@ mod tests { // ── composer_home_dir tests ─────────────────────────────────────────────── + /// Guards env-var mutations so the three composer_home_dir tests + /// cannot race each other when `cargo test` runs them in parallel. + static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(()); + #[test] fn test_composer_home_dir_from_env() { - // SAFETY: test-only; single-threaded env mutation + let _lock = ENV_MUTEX.lock().unwrap(); + // SAFETY: test-only; protected by ENV_MUTEX unsafe { std::env::set_var("COMPOSER_HOME", "/tmp/test-composer-home"); } let result = composer_home_dir().unwrap(); - assert_eq!(result, PathBuf::from("/tmp/test-composer-home")); - // SAFETY: cleanup unsafe { std::env::remove_var("COMPOSER_HOME"); } + assert_eq!(result, PathBuf::from("/tmp/test-composer-home")); } #[test] fn test_composer_home_dir_xdg() { - // SAFETY: test-only; single-threaded env mutation + let _lock = ENV_MUTEX.lock().unwrap(); + // SAFETY: test-only; protected by ENV_MUTEX unsafe { std::env::remove_var("COMPOSER_HOME"); std::env::set_var("XDG_CONFIG_HOME", "/tmp/test-xdg-config"); } let result = composer_home_dir().unwrap(); - assert_eq!(result, PathBuf::from("/tmp/test-xdg-config/composer")); - // SAFETY: cleanup unsafe { std::env::remove_var("XDG_CONFIG_HOME"); } + assert_eq!(result, PathBuf::from("/tmp/test-xdg-config/composer")); } #[test] fn test_composer_home_dir_default() { - // SAFETY: test-only; single-threaded env mutation + let _lock = ENV_MUTEX.lock().unwrap(); + // SAFETY: test-only; protected by ENV_MUTEX unsafe { std::env::remove_var("COMPOSER_HOME"); std::env::remove_var("XDG_CONFIG_HOME"); |
