diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-07-24 20:22:58 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-07-24 20:22:58 +0900 |
| commit | 8c316f2c5e93888f4b3a5b8c18df3373b54fa816 (patch) | |
| tree | 06455cd54b6a41febabab13ba76c9d92406b57fe | |
| parent | 08a99447445167e2b2eeee9c81fc89d5044bfe6d (diff) | |
| download | php-shirabe-8c316f2c5e93888f4b3a5b8c18df3373b54fa816.tar.gz php-shirabe-8c316f2c5e93888f4b3a5b8c18df3373b54fa816.tar.zst php-shirabe-8c316f2c5e93888f4b3a5b8c18df3373b54fa816.zip | |
fix(dependency-resolver): query real PHP for ext-* version/loaded checks
Two shim gaps made the extension-related branches of solver-problem
messages wrong:
- phpversion($ext) with a non-empty extension can't be known statically
(it's shirabe-php-shim's todo!()); Problem::get_missing_package_reason
now calls shirabe_php_rpc::phpversion, the same RPC bridge
platform::runtime::Runtime::get_extension_version already uses.
- extension_loaded's hardcoded allowlist was missing "pcre", a mandatory
always-compiled-in PHP extension, so ext-pcre was misreported as
"missing from your system" instead of "disabled by your platform
config" whenever a platform override disabled it.
Also fix XdebugHandler::getAllIniFiles() always returning `[""]`
(a php-runtime stub): create_extension_hint()'s early-return guard
(`paths[0] empty && len==1`) fired unconditionally, silently dropping
the entire "To enable extensions..." hint from every solver-problem
message that mentions missing extensions. shirabe-external-packages
can't depend on shirabe-php-rpc (shirabe-php-rpc already depends on
shirabe-external-packages), so IniHelper::get_all() queries a new
get_all_ini_files RPC command directly instead of going through the
stub.
This exposed that XdebugHandler is never constructed with a name
because bin/composer's restart-without-Xdebug bootstrap was never
ported to main.rs, making COMPOSER_ORIGINAL_INIS-driven behavior
unreachable; documented with a TODO(phase-c) and updated
ini_helper_test.rs's ignore reasons (and ignored test_with_no_ini,
which only passed before by coincidence with the old stub's constant
output) to match.
| -rw-r--r-- | crates/shirabe-external-packages/src/composer/xdebug_handler/xdebug_handler.rs | 14 | ||||
| -rw-r--r-- | crates/shirabe-php-rpc/php/worker.php | 8 | ||||
| -rw-r--r-- | crates/shirabe-php-rpc/src/lib.rs | 12 | ||||
| -rw-r--r-- | crates/shirabe-php-shim/src/runtime.rs | 1 | ||||
| -rw-r--r-- | crates/shirabe/src/dependency_resolver/problem.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/main.rs | 7 | ||||
| -rw-r--r-- | crates/shirabe/src/util/ini_helper.rs | 8 | ||||
| -rw-r--r-- | crates/shirabe/tests/installer_test.rs | 16 | ||||
| -rw-r--r-- | crates/shirabe/tests/util/ini_helper_test.rs | 18 |
9 files changed, 66 insertions, 22 deletions
diff --git a/crates/shirabe-external-packages/src/composer/xdebug_handler/xdebug_handler.rs b/crates/shirabe-external-packages/src/composer/xdebug_handler/xdebug_handler.rs index 01f8eb78..3bbe9014 100644 --- a/crates/shirabe-external-packages/src/composer/xdebug_handler/xdebug_handler.rs +++ b/crates/shirabe-external-packages/src/composer/xdebug_handler/xdebug_handler.rs @@ -19,10 +19,16 @@ impl XdebugHandler { pub fn get_all_ini_files() -> Vec<String> { // TODO(php-runtime) - // No XdebugHandler is ever constructed (`self::$name` stays null) so the - // COMPOSER_ORIG_INIS env lookup is skipped, and without a PHP runtime - // `php_ini_loaded_file()` is `false` and `php_ini_scanned_files()` is `false`, - // so PHP would return `[(string) false]` = `[""]`. + // No XdebugHandler is ever constructed (`self::$name` stays null), because the + // `new XdebugHandler('Composer'); $xdebug->check();` bootstrap in `bin/composer` is not + // ported (see the TODO(phase-c) at the top of shirabe's main.rs), so the + // COMPOSER_ORIGINAL_INIS env-var branch is unreachable here. + // + // shirabe-external-packages cannot depend on shirabe-php-rpc (the reverse dependency + // would cycle, since shirabe-php-rpc already depends on shirabe-external-packages), so + // callers that need the real PHP runtime's ini files (php_ini_loaded_file() / + // php_ini_scanned_files()) query shirabe_php_rpc directly instead of going through this + // stub; see crate::util::ini_helper::IniHelper::get_all. vec![String::new()] } } diff --git a/crates/shirabe-php-rpc/php/worker.php b/crates/shirabe-php-rpc/php/worker.php index d7d8f251..060373d5 100644 --- a/crates/shirabe-php-rpc/php/worker.php +++ b/crates/shirabe-php-rpc/php/worker.php @@ -13,6 +13,14 @@ $dispatch = [ 'curl_version' => static fn($arg) => function_exists('curl_version') ? (curl_version()['version'] ?? null) : null, 'phpversion' => static fn($name) => phpversion($name), 'get_loaded_extensions' => static fn($arg) => implode(',', get_loaded_extensions()), + 'get_all_ini_files' => static function ($arg) { + $paths = [(string) php_ini_loaded_file()]; + $scanned = php_ini_scanned_files(); + if ($scanned !== false) { + $paths = array_merge($paths, array_map('trim', explode(',', $scanned))); + } + return implode(',', $paths); + }, 'extension_info' => static function ($name) { if (!extension_loaded($name)) { return ''; diff --git a/crates/shirabe-php-rpc/src/lib.rs b/crates/shirabe-php-rpc/src/lib.rs index d939829a..dde91d3c 100644 --- a/crates/shirabe-php-rpc/src/lib.rs +++ b/crates/shirabe-php-rpc/src/lib.rs @@ -88,6 +88,18 @@ pub fn get_loaded_extensions() -> Vec<String> { } } +/// `Composer\XdebugHandler\XdebugHandler::getAllIniFiles()` (minus the `self::$name` branch, +/// which is unreachable since this port never constructs an XdebugHandler): `[(string) +/// php_ini_loaded_file()]` merged with the trimmed, comma-split `php_ini_scanned_files()` list +/// when scanning is active. Paths are joined with `,` on the PHP side and split back here; real +/// ini paths never contain a comma (same assumption `get_loaded_extensions` makes). +pub fn get_all_ini_files() -> Vec<String> { + match call("get_all_ini_files", "") { + PhpMixed::String(s) => s.split(',').map(|s| s.to_string()).collect(), + other => panic!("PHP RPC: `get_all_ini_files` did not return a string: {other:?}"), + } +} + const GLUE_SCRIPT: &str = include_str!("../php/worker.php"); struct Worker { diff --git a/crates/shirabe-php-shim/src/runtime.rs b/crates/shirabe-php-shim/src/runtime.rs index fa7a728c..c4bd62c9 100644 --- a/crates/shirabe-php-shim/src/runtime.rs +++ b/crates/shirabe-php-shim/src/runtime.rs @@ -162,6 +162,7 @@ pub fn extension_loaded(name: &str) -> bool { | "intl" | "mbstring" | "openssl" + | "pcre" | "zip" | "zlib" ) diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs index 855638cf..5ff949a9 100644 --- a/crates/shirabe/src/dependency_resolver/problem.rs +++ b/crates/shirabe/src/dependency_resolver/problem.rs @@ -459,7 +459,9 @@ impl Problem { Self::constraint_to_text(constraint) ); - let runtime_version = phpversion(&ext); + // Per-extension version info can't be known statically; query the real PHP + // runtime via the RPC bridge, same as platform::runtime::Runtime::get_extension_version. + let runtime_version = shirabe_php_rpc::phpversion(&ext); let effective_version = match runtime_version { None => "0".to_string(), Some(ref v) => v.clone(), diff --git a/crates/shirabe/src/main.rs b/crates/shirabe/src/main.rs index 2ae6d903..6eb8e4cb 100644 --- a/crates/shirabe/src/main.rs +++ b/crates/shirabe/src/main.rs @@ -3,6 +3,13 @@ use shirabe_php_shim::{PHP_ENV, PHP_SERVER}; fn main() { + // TODO(phase-c): PHP: `$xdebug = new XdebugHandler('Composer'); $xdebug->check(); unset($xdebug);` + // (restart the process without Xdebug loaded, for performance) is not ported. Since no + // XdebugHandler is ever constructed, `self::$name` never gets set, so the + // COMPOSER_ORIGINAL_INIS-env-var branch of XdebugHandler::getAllIniFiles() (see + // shirabe-external-packages' xdebug_handler.rs and shirabe/src/util/ini_helper.rs) is + // unreachable in this port, not merely unexercised by current tests. + // Take the $_ENV / $_SERVER snapshots before any putenv() mutates the real environment. // See `docs/dev/env-vars-porting.md` for details. std::sync::LazyLock::force(&PHP_ENV); diff --git a/crates/shirabe/src/util/ini_helper.rs b/crates/shirabe/src/util/ini_helper.rs index 57894113..c36b9189 100644 --- a/crates/shirabe/src/util/ini_helper.rs +++ b/crates/shirabe/src/util/ini_helper.rs @@ -1,13 +1,15 @@ //! ref: composer/src/Composer/Util/IniHelper.php -use shirabe_external_packages::composer::xdebug_handler::XdebugHandler; - pub struct IniHelper; impl IniHelper { /// Returns an array of php.ini locations with at least one entry. pub fn get_all() -> Vec<String> { - XdebugHandler::get_all_ini_files() + // PHP: XdebugHandler::getAllIniFiles(). shirabe_external_packages::XdebugHandler's port + // of that method can't reach the PHP RPC bridge (shirabe-php-rpc already depends on + // shirabe-external-packages, so the reverse dependency would cycle), so query the real + // PHP runtime's loaded/scanned ini files here instead. + shirabe_php_rpc::get_all_ini_files() } /// Describes the location of the loaded php.ini file(s). diff --git a/crates/shirabe/tests/installer_test.rs b/crates/shirabe/tests/installer_test.rs index 1e25646b..311fbb12 100644 --- a/crates/shirabe/tests/installer_test.rs +++ b/crates/shirabe/tests/installer_test.rs @@ -1396,7 +1396,7 @@ pool_optimizer_test! { pool_optimizer_install_without_lock => "install-without-lock.test"; pool_optimizer_load_replaced_package_if_replacer_dropped => "load-replaced-package-if-replacer-dropped.test"; pool_optimizer_outdated_lock_file_fails_install => "outdated-lock-file-fails-install.test"; - pool_optimizer_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; + pool_optimizer_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test"; pool_optimizer_partial_update_always_updates_symlinked_path_repos => "partial-update-always-updates-symlinked-path-repos.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; pool_optimizer_partial_update_downgrades_non_allow_listed_unstable => "partial-update-downgrades-non-allow-listed-unstable.test"; pool_optimizer_partial_update_forces_dev_reference_from_lock_for_non_updated_packages => "partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; @@ -1413,7 +1413,7 @@ pool_optimizer_test! { pool_optimizer_partial_update_with_deps_warns_root => "partial-update-with-deps-warns-root.test"; pool_optimizer_partial_update_with_symlinked_path_repos => "partial-update-with-symlinked-path-repos.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; pool_optimizer_partial_update_without_lock => "partial-update-without-lock.test"; - pool_optimizer_platform_ext_solver_problems => "platform-ext-solver-problems.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; + pool_optimizer_platform_ext_solver_problems => "platform-ext-solver-problems.test"; pool_optimizer_plugins_are_installed_first => "plugins-are-installed-first.test"; pool_optimizer_prefer_lowest_branches => "prefer-lowest-branches.test"; pool_optimizer_problems_reduce_versions => "problems-reduce-versions.test"; @@ -1447,8 +1447,8 @@ pool_optimizer_test! { pool_optimizer_root_alias_gets_loaded_for_locked_pkgs => "root-alias-gets-loaded-for-locked-pkgs.test"; pool_optimizer_root_requirements_do_not_affect_locked_versions => "root-requirements-do-not-affect-locked-versions.test"; pool_optimizer_solver_problem_with_hash_in_branch => "solver-problem-with-hash-in-branch.test"; - pool_optimizer_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; - pool_optimizer_solver_problems => "solver-problems.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; + pool_optimizer_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test"; + pool_optimizer_solver_problems => "solver-problems.test"; pool_optimizer_suggest_installed => "suggest-installed.test"; pool_optimizer_suggest_prod_nolock => "suggest-prod-nolock.test"; pool_optimizer_suggest_prod => "suggest-prod.test"; @@ -1586,7 +1586,7 @@ raw_pool_test! { raw_pool_install_without_lock => "install-without-lock.test"; raw_pool_load_replaced_package_if_replacer_dropped => "load-replaced-package-if-replacer-dropped.test"; raw_pool_outdated_lock_file_fails_install => "outdated-lock-file-fails-install.test"; - raw_pool_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; + raw_pool_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test"; raw_pool_partial_update_always_updates_symlinked_path_repos => "partial-update-always-updates-symlinked-path-repos.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; raw_pool_partial_update_downgrades_non_allow_listed_unstable => "partial-update-downgrades-non-allow-listed-unstable.test"; raw_pool_partial_update_forces_dev_reference_from_lock_for_non_updated_packages => "partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; @@ -1603,7 +1603,7 @@ raw_pool_test! { raw_pool_partial_update_with_deps_warns_root => "partial-update-with-deps-warns-root.test"; raw_pool_partial_update_with_symlinked_path_repos => "partial-update-with-symlinked-path-repos.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; raw_pool_partial_update_without_lock => "partial-update-without-lock.test"; - raw_pool_platform_ext_solver_problems => "platform-ext-solver-problems.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; + raw_pool_platform_ext_solver_problems => "platform-ext-solver-problems.test"; raw_pool_plugins_are_installed_first => "plugins-are-installed-first.test"; raw_pool_prefer_lowest_branches => "prefer-lowest-branches.test"; raw_pool_problems_reduce_versions => "problems-reduce-versions.test"; @@ -1637,8 +1637,8 @@ raw_pool_test! { raw_pool_root_alias_gets_loaded_for_locked_pkgs => "root-alias-gets-loaded-for-locked-pkgs.test"; raw_pool_root_requirements_do_not_affect_locked_versions => "root-requirements-do-not-affect-locked-versions.test"; raw_pool_solver_problem_with_hash_in_branch => "solver-problem-with-hash-in-branch.test"; - raw_pool_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; - raw_pool_solver_problems => "solver-problems.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; + raw_pool_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test"; + raw_pool_solver_problems => "solver-problems.test"; raw_pool_suggest_installed => "suggest-installed.test"; raw_pool_suggest_prod_nolock => "suggest-prod-nolock.test"; raw_pool_suggest_prod => "suggest-prod.test"; diff --git a/crates/shirabe/tests/util/ini_helper_test.rs b/crates/shirabe/tests/util/ini_helper_test.rs index 76705a09..0b3835a0 100644 --- a/crates/shirabe/tests/util/ini_helper_test.rs +++ b/crates/shirabe/tests/util/ini_helper_test.rs @@ -43,6 +43,9 @@ fn set_env(paths: &[&str]) { } #[test] +#[ignore = "XdebugHandler is never constructed with a name (the bin/composer restart-without-Xdebug \ +bootstrap is unported, see TODO(phase-c) in shirabe's main.rs), so IniHelper::get_all() queries \ +the real PHP runtime directly instead of consulting COMPOSER_ORIGINAL_INIS"] fn test_with_no_ini() { let paths = [""]; @@ -55,8 +58,9 @@ fn test_with_no_ini() { } #[test] -#[ignore = "XdebugHandler::get_all_ini_files() is stubbed to always return [\"\"], ignoring \ -COMPOSER_ORIGINAL_INIS entirely"] +#[ignore = "XdebugHandler is never constructed with a name (the bin/composer restart-without-Xdebug \ +bootstrap is unported, see TODO(phase-c) in shirabe's main.rs), so IniHelper::get_all() queries \ +the real PHP runtime directly instead of consulting COMPOSER_ORIGINAL_INIS"] fn test_with_loaded_ini_only() { let paths = ["loaded.ini"]; @@ -65,8 +69,9 @@ fn test_with_loaded_ini_only() { } #[test] -#[ignore = "XdebugHandler::get_all_ini_files() is stubbed to always return [\"\"], ignoring \ -COMPOSER_ORIGINAL_INIS entirely"] +#[ignore = "XdebugHandler is never constructed with a name (the bin/composer restart-without-Xdebug \ +bootstrap is unported, see TODO(phase-c) in shirabe's main.rs), so IniHelper::get_all() queries \ +the real PHP runtime directly instead of consulting COMPOSER_ORIGINAL_INIS"] fn test_with_loaded_ini_and_additional() { let paths = ["loaded.ini", "one.ini", "two.ini"]; @@ -79,8 +84,9 @@ fn test_with_loaded_ini_and_additional() { } #[test] -#[ignore = "XdebugHandler::get_all_ini_files() is stubbed to always return [\"\"], ignoring \ -COMPOSER_ORIGINAL_INIS entirely"] +#[ignore = "XdebugHandler is never constructed with a name (the bin/composer restart-without-Xdebug \ +bootstrap is unported, see TODO(phase-c) in shirabe's main.rs), so IniHelper::get_all() queries \ +the real PHP runtime directly instead of consulting COMPOSER_ORIGINAL_INIS"] fn test_without_loaded_ini_and_additional() { let paths = ["", "one.ini", "two.ini"]; |
