in hphp/hsl/src/legacy_fixme/coercions.php [213:430]
function __cast_and_compare(mixed $l, mixed $r, COMPARISON_TYPE $ctype)[]: int {
if ($r is bool && !(\HH\is_fun($l) || \HH\is_class_meth($l))) {
if (!($l is AnyArray<_, _>)) {
$l = (bool)$l;
} else if ($ctype === COMPARISON_TYPE::EQ) {
$l = !C\is_empty($l);
}
} else if ($r is null) {
if ($l is string) {
$r = '';
} else if (\is_object($l)) {
$l = true;
$r = false;
} else {
return __cast_and_compare($l, false, $ctype);
}
} else if (
\HH\is_fun($r) ||
\HH\is_fun($l) ||
\HH\is_class_meth($r) ||
\HH\is_class_meth($l)
) {
// no-op.
} else if ($r is num) {
if ($l is null) {
$l = false;
$r = (bool)$r;
} else if ($l is bool) {
$r = (bool)$r;
} else if ($l is string) {
$l = \HH\str_to_numeric($l) ?? 0;
} else if (
$l is resource || (\is_object($l) && !($l is \ConstCollection<_>))
) {
$l = $r is int ? (int)$l : (float)$l;
}
// if we're ==/!= an int and a float, convert both to float
if (
$ctype === COMPARISON_TYPE::EQ &&
$r is num &&
$l is num &&
$r is int !== $l is int
) {
$l = (float)$l;
$r = (float)$r;
}
} else if ($r is string) {
if ($l is null) {
$l = '';
} else if ($l is bool) {
$r = (bool)$r;
} else if ($l is num) {
$r = \HH\str_to_numeric($r) ?? 0;
return __cast_and_compare($l, $r, $ctype);
} else if (\is_object($l)) {
if ($l is \StringishObject && !($l is \ConstCollection<_>)) {
$l = (string)$l;
} else if (!($l is \ConstCollection<_>)) {
$l = true;
$r = false;
}
} else if ($l is resource) {
$l = (float)$l;
$r = (float)$r;
} else if (
$l is string &&
$ctype === COMPARISON_TYPE::EQ &&
\is_numeric($l) &&
\is_numeric($r)
) {
$l = \HH\str_to_numeric($l);
$r = \HH\str_to_numeric($r);
return __cast_and_compare($l, $r, $ctype);
}
} else if ($r is resource) {
if ($l is null) {
$l = false;
$r = true;
} else if ($l is bool) {
// @lint-ignore CAST_NON_PRIMITIVE 2fax
$r = (bool)$r;
} else if ($l is num) {
// @lint-ignore CAST_NON_PRIMITIVE 2fax
$r = $l is int ? (int)$r : (float)$r;
} else if ($l is string) {
$l = (float)$l;
// @lint-ignore CAST_NON_PRIMITIVE 2fax
$r = (float)$r;
} else if (\is_object($l)) {
$l = true;
$r = false;
}
} else if ($r is AnyArray<_, _> && ($l is null || $l is bool)) {
if ($l is null) {
$l = false;
}
if ($ctype === COMPARISON_TYPE::EQ) {
$r = !C\is_empty($r);
}
} else if (
($r is vec<_> && $l is vec<_>) ||
(
$ctype === COMPARISON_TYPE::EQ &&
$r is \ConstVector<_> &&
$l is \ConstVector<_>
)
) {
if (C\count($l) !== C\count($r)) {
return C\count($l) > C\count($r) ? 1 : -1;
}
foreach ($l as $i => $li) {
$ri = $r[$i];
$res = __cast_and_compare($li, $ri, $ctype);
if ($res !== 0) {
if (
$ctype === COMPARISON_TYPE::GT &&
\is_object($ri) &&
\is_object($li) &&
(\get_class($li) !== \get_class($ri) || $li is \Closure) &&
!($li is \DateTimeInterface && $ri is \DateTimeInterface)
) {
// flip the result :p
return $res === -1 ? 1 : -1;
}
return $res;
}
}
return 0;
} else if (
$ctype === COMPARISON_TYPE::EQ &&
(
($r is dict<_, _> && $l is dict<_, _>) ||
($r is \ConstMap<_, _> && $l is \ConstMap<_, _>) ||
($r is \ConstSet<_> && $l is \ConstSet<_>)
)
) {
if (C\count($l) !== C\count($r)) return 1;
foreach ($l as $i => $li) {
if (
/* HH_FIXME[4324] I've just confirmed this is safe */
/* HH_FIXME[4005] Set is KeyedContainer... */
!C\contains_key($r, $i) || __cast_and_compare($li, $r[$i], $ctype) !== 0
) {
return 1;
}
}
return 0;
} else if (\is_object($r)) {
if (
$l is string && $r is \StringishObject && !($r is \ConstCollection<_>)
) {
$r = (string)$r;
} else if (
$l is null ||
$l is resource ||
($l is string && !($r is \ConstCollection<_>))
) {
$l = false;
$r = true;
} else if ($l is num && !($r is \ConstCollection<_>)) {
// this probably throws, but sometimes it doesn't!
$r = $l is int ? (int)$r : (float)$r;
} else if ($l is bool) {
$r = (bool)$r;
} else if (
\is_object($l) &&
!($l is \ConstCollection<_> || $r is \ConstCollection<_>) &&
$l !== $r &&
!($l is \DateTimeInterface && $r is \DateTimeInterface)
) {
if (\get_class($l) !== \get_class($r) || $l is \Closure) {
return $ctype === COMPARISON_TYPE::GT ? -1 : 1;
} else if (!($l is \SimpleXMLElement)) {
$l = \HH\object_prop_array($l);
$r = \HH\object_prop_array($r);
if (C\count($l) !== C\count($r)) {
return C\count($l) > C\count($r) ? 1 : -1;
}
$loop_var = $ctype === COMPARISON_TYPE::GT ? $r : $l;
$other_var = $ctype === COMPARISON_TYPE::GT ? $l : $r;
foreach ($loop_var as $i => $_) {
if (!C\contains_key($other_var, $i)) {
// dyn prop in a but not b
return $ctype === COMPARISON_TYPE::GT ? -1 : 1;
}
$li = $l[$i];
$ri = $r[$i];
$res = __cast_and_compare($li, $ri, $ctype);
if ($res !== 0) {
if (
($li is float && Math\is_nan($li)) ||
($ri is float && Math\is_nan($ri))
) {
// in the case of NAN && GT, straight up flip the result
return $ctype === COMPARISON_TYPE::GT && $res === -1 ? 1 : -1;
}
return $res;
}
}
return 0;
}
}
}
if (($l is float && Math\is_nan($l)) || ($r is float && Math\is_nan($r))) {
// trigger exception if necessary
$_r = $ctype === COMPARISON_TYPE::EQ
? ($l as dynamic) == ($r as dynamic)
: ($l as dynamic) <=> ($r as dynamic);
return $ctype === COMPARISON_TYPE::LT ? 1 : -1;
}
if ($ctype === COMPARISON_TYPE::EQ) {
return (int)($l != $r);
}
return ($l as dynamic) <=> ($r as dynamic);
}