aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-07-20 16:51:45 +0900
committernsfisis <nsfisis@gmail.com>2026-07-20 16:51:45 +0900
commita2110fa811c8f6c7d1c45a83c6fd1077eb6f3461 (patch)
tree620f3cea4ef0c99b5e4a0b7878a59b9e7dbabe36
parent37ed5b8c6d4cda30e668d0221eb281431dbc8c67 (diff)
downloadphp-shirabe-a2110fa811c8f6c7d1c45a83c6fd1077eb6f3461.tar.gz
php-shirabe-a2110fa811c8f6c7d1c45a83c6fd1077eb6f3461.tar.zst
php-shirabe-a2110fa811c8f6c7d1c45a83c6fd1077eb6f3461.zip
fix(php-shim): use PhpMixed::to_bool() for PHP truthy casts
Several call sites coerced PhpMixed to bool via `.as_bool()` (which only matches a literal Bool variant) where the corresponding PHP code does a plain `(bool)` cast or truthy check (isset()/array_key_exists() + implicit bool conversion). This silently dropped truthy non-bool values (e.g. String("true"), String("1"), Int(1)) to their unwrap_or default instead of PHP's actual truthy result. Switched these sites to PhpMixed::to_bool(), which implements PHP's full truthy-cast rules.
-rw-r--r--crates/shirabe/src/advisory/audit_config.rs9
-rw-r--r--crates/shirabe/src/config.rs13
-rw-r--r--crates/shirabe/src/dependency_resolver/default_policy.rs4
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs5
-rw-r--r--crates/shirabe/src/downloader/path_downloader.rs2
-rw-r--r--crates/shirabe/src/downloader/svn_downloader.rs8
-rw-r--r--crates/shirabe/src/package/complete_package.rs6
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs5
-rw-r--r--crates/shirabe/src/package/loader/root_package_loader.rs4
-rw-r--r--crates/shirabe/src/repository/vcs/github_driver.rs3
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs11
-rw-r--r--crates/shirabe/src/util/platform.rs22
-rw-r--r--crates/shirabe/src/util/remote_filesystem.rs8
-rw-r--r--crates/shirabe/tests/config_test.rs2
14 files changed, 41 insertions, 61 deletions
diff --git a/crates/shirabe/src/advisory/audit_config.rs b/crates/shirabe/src/advisory/audit_config.rs
index e5bf6985..71730317 100644
--- a/crates/shirabe/src/advisory/audit_config.rs
+++ b/crates/shirabe/src/advisory/audit_config.rs
@@ -168,17 +168,20 @@ impl AuditConfig {
let block_insecure = audit_config
.and_then(|m| m.get("block-insecure"))
- .and_then(|v| v.as_bool())
+ .and_then(|v| v.as_opt())
+ .map(|v| v.to_bool())
.unwrap_or(true);
let block_abandoned = audit_config
.and_then(|m| m.get("block-abandoned"))
- .and_then(|v| v.as_bool())
+ .and_then(|v| v.as_opt())
+ .map(|v| v.to_bool())
.unwrap_or(false);
let ignore_unreachable = audit_config
.and_then(|m| m.get("ignore-unreachable"))
- .and_then(|v| v.as_bool())
+ .and_then(|v| v.as_opt())
+ .map(|v| v.to_bool())
.unwrap_or(false);
Ok(Self::new(
diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs
index 8f78ecca..c389063b 100644
--- a/crates/shirabe/src/config.rs
+++ b/crates/shirabe/src/config.rs
@@ -637,8 +637,7 @@ impl Config {
};
Ok(PhpMixed::Bool(
- val.as_string() != Some("false")
- && val.as_bool().unwrap_or_else(|| !val.is_null()),
+ val.as_string() != Some("false") && val.to_bool(),
))
}
@@ -653,7 +652,7 @@ impl Config {
let v = self.config.get(key).cloned().unwrap_or(PhpMixed::Null);
Ok(PhpMixed::Bool(
- v.as_string() != Some("false") && v.as_bool().unwrap_or(false),
+ v.as_string() != Some("false") && v.to_bool(),
))
}
@@ -1179,8 +1178,8 @@ impl Config {
.get("ssl")
.and_then(|v| v.as_array())
.and_then(|m| m.get("verify_peer"));
- if let Some(v) = verify_peer
- && v.as_bool() == Some(false)
+ if let Some(v) = verify_peer.and_then(|v| v.as_opt())
+ && !v.to_bool()
{
warning = Some("verify_peer".to_string());
}
@@ -1189,8 +1188,8 @@ impl Config {
.get("ssl")
.and_then(|v| v.as_array())
.and_then(|m| m.get("verify_peer_name"));
- if let Some(v) = verify_peer_name
- && v.as_bool() == Some(false)
+ if let Some(v) = verify_peer_name.and_then(|v| v.as_opt())
+ && !v.to_bool()
{
warning = match warning {
None => Some("verify_peer_name".to_string()),
diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs
index e7d02e24..24069466 100644
--- a/crates/shirabe/src/dependency_resolver/default_policy.rs
+++ b/crates/shirabe/src/dependency_resolver/default_policy.rs
@@ -6,6 +6,7 @@ use crate::package::BasePackageHandle;
use crate::package::STABILITIES;
use crate::util::Platform;
use indexmap::IndexMap;
+use shirabe_php_shim::PhpMixed;
use shirabe_semver::CompilingMatcher;
use shirabe_semver::constraint::SimpleConstraint;
@@ -31,8 +32,7 @@ impl DefaultPolicy {
prefer_lowest,
preferred_versions,
prefer_dev_over_prerelease: Platform::get_env("COMPOSER_PREFER_DEV_OVER_PRERELEASE")
- .map(|v| !v.is_empty())
- .unwrap_or(false),
+ .is_some_and(|v| PhpMixed::String(v).to_bool()),
preferred_package_result_cache_per_pool: std::cell::RefCell::new(IndexMap::new()),
sorting_cache_per_pool: std::cell::RefCell::new(IndexMap::new()),
}
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index 7ca31e24..eaedc453 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -896,8 +896,9 @@ impl VcsDownloader for GitDownloader {
];
let transport_options = package.get_transport_options();
if let Some(git_opts) = transport_options.get("git").and_then(|v| v.as_array())
- && let Some(single) = git_opts.get("single_use_clone").and_then(|v| v.as_bool())
- && single
+ && git_opts
+ .get("single_use_clone")
+ .is_some_and(|v| v.to_bool())
{
clone_flags = vec![];
}
diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs
index eac5a859..d6c5fee7 100644
--- a/crates/shirabe/src/downloader/path_downloader.rs
+++ b/crates/shirabe/src/downloader/path_downloader.rs
@@ -131,7 +131,7 @@ impl PathDownloader {
let mut allowed_strategies = vec![Self::STRATEGY_SYMLINK, Self::STRATEGY_MIRROR];
let mirror_path_repos = Platform::get_env("COMPOSER_MIRROR_PATH_REPOS");
- if mirror_path_repos.is_some_and(|v| !v.is_empty()) {
+ if mirror_path_repos.is_some_and(|v| PhpMixed::String(v).to_bool()) {
current_strategy = Self::STRATEGY_MIRROR;
}
diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs
index 576a2676..e72bd40a 100644
--- a/crates/shirabe/src/downloader/svn_downloader.rs
+++ b/crates/shirabe/src/downloader/svn_downloader.rs
@@ -164,12 +164,8 @@ impl VcsDownloader for SvnDownloader {
let repo_ref = repo.borrow();
if let Some(vcs_repo) = repo_ref.as_any().downcast_ref::<VcsRepository>() {
let repo_config = vcs_repo.get_repo_config();
- if repo_config.contains_key("svn-cache-credentials")
- && let Some(val) = repo_config
- .get("svn-cache-credentials")
- .and_then(|v| v.as_bool())
- {
- self.cache_credentials.set(val);
+ if let Some(val) = repo_config.get("svn-cache-credentials") {
+ self.cache_credentials.set(val.to_bool());
}
}
}
diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs
index ec9986c9..b2c3978c 100644
--- a/crates/shirabe/src/package/complete_package.rs
+++ b/crates/shirabe/src/package/complete_package.rs
@@ -127,11 +127,7 @@ impl CompletePackageInterface for CompletePackage {
}
fn is_abandoned(&self) -> bool {
- match &self.abandoned {
- PhpMixed::Bool(b) => *b,
- PhpMixed::String(s) => !s.is_empty(),
- _ => false,
- }
+ self.abandoned.to_bool()
}
fn set_abandoned(&mut self, abandoned: PhpMixed) {
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 07937c31..3a78ff76 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -132,10 +132,7 @@ fn php_to_mirrors(value: &PhpMixed) -> Vec<Mirror> {
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string(),
- preferred: m
- .get("preferred")
- .and_then(|v| v.as_bool())
- .unwrap_or(false),
+ preferred: m.get("preferred").is_some_and(|v| v.to_bool()),
}),
_ => None,
})
diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs
index 95cd1225..284bb175 100644
--- a/crates/shirabe/src/package/loader/root_package_loader.rs
+++ b/crates/shirabe/src/package/loader/root_package_loader.rs
@@ -229,8 +229,8 @@ impl RootPackageLoader {
real_package.set_stability_flags(stability_flags);
real_package.set_references(references);
- if let Some(prefer_stable) = config.get("prefer-stable").and_then(|v| v.as_bool()) {
- real_package.set_prefer_stable(prefer_stable);
+ if let Some(prefer_stable) = config.get("prefer-stable").and_then(|v| v.as_opt()) {
+ real_package.set_prefer_stable(prefer_stable.to_bool());
}
if let Some(pkg_config) = config.get("config").and_then(|v| v.as_array()) {
diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs
index 94daf05c..c7fe5266 100644
--- a/crates/shirabe/src/repository/vcs/github_driver.rs
+++ b/crates/shirabe/src/repository/vcs/github_driver.rs
@@ -160,8 +160,7 @@ impl GitHubDriver {
.inner
.repo_config
.get("no-api")
- .and_then(|v| v.as_bool())
- == Some(true)
+ .is_some_and(|v| v.to_bool())
{
self.setup_git_driver(&self.inner.url.clone())?;
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 5f1faa65..ecf4a48a 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -15,7 +15,7 @@ use chrono::{DateTime, FixedOffset, Utc};
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_php_shim::{
- PhpMixed, RuntimeException, array_key_exists, php_regex, stripos, strrpos, strtr, substr, trim,
+ PhpMixed, RuntimeException, php_regex, stripos, strrpos, strtr, substr, trim,
};
#[derive(Debug)]
@@ -85,13 +85,8 @@ impl SvnDriver {
if let Some(PhpMixed::String(v)) = self.inner.repo_config.get("tags-path").cloned() {
self.tags_path = v;
}
- if array_key_exists("svn-cache-credentials", &self.inner.repo_config) {
- self.cache_credentials = self
- .inner
- .repo_config
- .get("svn-cache-credentials")
- .and_then(|v| v.as_bool())
- .unwrap_or(false);
+ if let Some(v) = self.inner.repo_config.get("svn-cache-credentials") {
+ self.cache_credentials = v.to_bool();
}
if let Some(PhpMixed::String(v)) = self.inner.repo_config.get("package-path").cloned() {
self.package_path = format!("/{}", trim(&v, Some("/")));
diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs
index 81f2adbb..259d5c95 100644
--- a/crates/shirabe/src/util/platform.rs
+++ b/crates/shirabe/src/util/platform.rs
@@ -116,12 +116,10 @@ impl Platform {
.unwrap_or("");
// Treat HOME as an alias for USERPROFILE on Windows for legacy reasons
if Platform::is_windows() && var == "HOME" {
- if Platform::get_env("HOME").is_some() {
- return format!(
- "{}{}",
- Platform::get_env("HOME").unwrap_or_default(),
- path_part,
- );
+ let home =
+ Platform::get_env("HOME").filter(|v| PhpMixed::String(v.clone()).to_bool());
+ if let Some(home) = home {
+ return format!("{}{}", home, path_part);
}
return format!(
@@ -189,9 +187,7 @@ impl Platform {
.ok()
.flatten()
.unwrap_or_default();
- if !(ini_get("open_basedir")
- .map(|s| !s.is_empty())
- .unwrap_or(false))
+ if !ini_get("open_basedir").is_some_and(|s| PhpMixed::String(s).to_bool())
&& is_readable("/proc/version")
&& stripos(&file_contents, "microsoft").is_some()
&& !Self::is_docker()
@@ -217,10 +213,7 @@ impl Platform {
}
// cannot check so assume no
- if ini_get("open_basedir")
- .map(|s| !s.is_empty())
- .unwrap_or(false)
- {
+ if ini_get("open_basedir").is_some_and(|s| PhpMixed::String(s).to_bool()) {
*cached = Some(false);
return false;
}
@@ -274,8 +267,7 @@ impl Platform {
*use_mb_string = Some(
function_exists("mb_strlen")
&& ini_get("mbstring.func_overload")
- .map(|s| !s.is_empty())
- .unwrap_or(false),
+ .is_some_and(|s| PhpMixed::String(s).to_bool()),
);
}
diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs
index 08e8581f..103a94a7 100644
--- a/crates/shirabe/src/util/remote_filesystem.rs
+++ b/crates/shirabe/src/util/remote_filesystem.rs
@@ -194,8 +194,12 @@ impl RemoteFilesystem {
self.redirects = 1; // The first request counts.
let mut temp_additional_options = additional_options.clone();
- if let Some(v) = temp_additional_options.get("retry-auth-failure").cloned() {
- retry_auth_failure = v.as_bool().unwrap_or(true);
+ if let Some(v) = temp_additional_options
+ .get("retry-auth-failure")
+ .and_then(|v| v.as_opt())
+ .cloned()
+ {
+ retry_auth_failure = v.to_bool();
temp_additional_options.shift_remove("retry-auth-failure");
}
diff --git a/crates/shirabe/tests/config_test.rs b/crates/shirabe/tests/config_test.rs
index 2d80b625..918da8c2 100644
--- a/crates/shirabe/tests/config_test.rs
+++ b/crates/shirabe/tests/config_test.rs
@@ -520,7 +520,6 @@ fn test_prohibited_urls_warning_verify_peer() {
.unwrap();
}
-#[ignore = "Config::get's disable-tls/secure-http/use-github-api/lock branch casts via v.as_bool().unwrap_or(false) instead of PhpMixed::to_bool() (PHP's (bool) cast), so a truthy String(\"true\") is read back as false"]
#[test]
fn test_disable_tls_can_be_overridden() {
let mut config = Config::new(true, None);
@@ -547,7 +546,6 @@ fn test_process_timeout() {
assert_eq!(PhpMixed::Int(0), result);
}
-#[ignore = "Config::get's cache-read-only/htaccess-protect branch casts via val.as_bool().unwrap_or_else(|| !val.is_null()) instead of PhpMixed::to_bool() (PHP's (bool) cast), so String(\"0\") from COMPOSER_HTACCESS_PROTECT is read back as true instead of false"]
#[test]
#[serial]
fn test_htaccess_protect() {