aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/src/array.rs69
1 files changed, 64 insertions, 5 deletions
diff --git a/crates/shirabe-php-shim/src/array.rs b/crates/shirabe-php-shim/src/array.rs
index 7c400fb..280ec64 100644
--- a/crates/shirabe-php-shim/src/array.rs
+++ b/crates/shirabe-php-shim/src/array.rs
@@ -591,13 +591,72 @@ pub fn in_array(needle: PhpMixed, haystack: &PhpMixed, strict: bool) -> bool {
_ => return false,
};
- if !strict {
- // TODO(phase-c): non-strict in_array needs PHP's loose `==` comparison semantics. Only the
- // strict path is implemented; loose comparison is deferred rather than approximated.
- todo!("non-strict in_array (PHP loose comparison)");
+ if strict {
+ values.iter().any(|value| **value == needle)
+ } else {
+ values.iter().any(|value| loose_eq(value, &needle))
+ }
+}
+
+/// PHP numeric-string-aware conversion to a number, used by loose comparison.
+fn loose_to_number(s: &str) -> Option<f64> {
+ if crate::var::is_numeric_string(s) {
+ s.trim().parse::<f64>().ok()
+ } else {
+ None
}
+}
- values.iter().any(|value| **value == needle)
+fn loose_num_to_string(value: &PhpMixed) -> String {
+ match value {
+ PhpMixed::Int(i) => i.to_string(),
+ PhpMixed::Float(f) => f.to_string(),
+ _ => String::new(),
+ }
+}
+
+fn loose_num_value(value: &PhpMixed) -> f64 {
+ match value {
+ PhpMixed::Int(i) => *i as f64,
+ PhpMixed::Float(f) => *f,
+ _ => 0.0,
+ }
+}
+
+/// PHP `==` loose comparison.
+pub fn loose_eq(a: &PhpMixed, b: &PhpMixed) -> bool {
+ use PhpMixed::*;
+ match (a, b) {
+ (Null, Null) => true,
+ // null compares loosely against the "empty"/false-y value of the other type.
+ (Null, other) | (other, Null) => !crate::var::to_bool(other),
+ // If either operand is a bool, both are converted to bool.
+ (Bool(x), other) => *x == crate::var::to_bool(other),
+ (other, Bool(y)) => crate::var::to_bool(other) == *y,
+ (Int(x), Int(y)) => x == y,
+ (Int(_) | Float(_), Int(_) | Float(_)) => loose_num_value(a) == loose_num_value(b),
+ (String(x), String(y)) => match (loose_to_number(x), loose_to_number(y)) {
+ (Some(nx), Some(ny)) => nx == ny,
+ _ => x == y,
+ },
+ (Int(_) | Float(_), String(s)) => match loose_to_number(s) {
+ Some(ns) => loose_num_value(a) == ns,
+ None => loose_num_to_string(a) == *s,
+ },
+ (String(s), Int(_) | Float(_)) => match loose_to_number(s) {
+ Some(ns) => loose_num_value(b) == ns,
+ None => *s == loose_num_to_string(b),
+ },
+ (List(x), List(y)) => {
+ x.len() == y.len() && x.iter().zip(y.iter()).all(|(p, q)| loose_eq(p, q))
+ }
+ (Array(x), Array(y)) | (Object(x), Object(y)) => {
+ x.len() == y.len()
+ && x.iter()
+ .all(|(k, v)| y.get(k).map(|w| loose_eq(v, w)).unwrap_or(false))
+ }
+ _ => false,
+ }
}
pub fn krsort<V>(array: &mut IndexMap<i64, V>) {