diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-07-24 20:23:28 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-07-24 20:23:28 +0900 |
| commit | 9a1838a32b77740f787a7a097af294842ed9cfdd (patch) | |
| tree | ba6c482b26a320e1b54276a8f1d05c92fe76771a /crates/shirabe-php-shim | |
| parent | 7392662eed92f00b41e173809fb51f6a21572003 (diff) | |
| download | php-shirabe-9a1838a32b77740f787a7a097af294842ed9cfdd.tar.gz php-shirabe-9a1838a32b77740f787a7a097af294842ed9cfdd.tar.zst php-shirabe-9a1838a32b77740f787a7a097af294842ed9cfdd.zip | |
fix(solver-problems): compare RULE_LEARNED sort keys like PHP's <=>
Problem::getPrettyString sorts same-priority reasons by
getSortableString(), whose RULE_LEARNED key is a '-'-joined literal id
string (e.g. "-95"). PHP's <=> compares two numeric strings
numerically, but the port used plain String::cmp (byte-wise), which
reverses relative order for same-length negative-number keys. Added
shirabe_php_shim::loosely_compare to approximate PHP's <=> for this
pattern (numeric compare when both sides parse as numbers, else byte
compare) and switched the sort comparator to use it.
The diagnosis this replaces (from the commit being amended) blamed
Pool package-id assignment order diverging from PHP under
COMPOSER_POOL_OPTIMIZER=0. That was disproven this session: direct
instrumentation of both PHP and the Rust port confirmed identical
relative package-id order, including on the ~335-package
github-issues-7665 fixture (ids matched up to a constant +2 offset
from a platform-mock package count difference). The sort comparator
was the actual bug, not package loading order.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim')
| -rw-r--r-- | crates/shirabe-php-shim/src/var.rs | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/crates/shirabe-php-shim/src/var.rs b/crates/shirabe-php-shim/src/var.rs index 513c3e88..dd0592f8 100644 --- a/crates/shirabe-php-shim/src/var.rs +++ b/crates/shirabe-php-shim/src/var.rs @@ -185,6 +185,27 @@ pub fn is_numeric_to_int(value: &PhpMixed) -> i64 { } } +/// Approximates PHP's `<=>` for two strings: if both are numeric strings, compare numerically +/// (as PHP does), otherwise fall back to a byte-wise comparison. +/// +/// TODO: this only covers the string/string case of PHP's loose comparison. PHP's `<=>` has many +/// more special-cased rules across other operand type combinations (bool, array, null, object, +/// numeric-string-vs-non-numeric-string, ...). Extend this if a new caller needs those. +pub fn loosely_compare(a: &str, b: &str) -> std::cmp::Ordering { + if is_numeric_string(a) && is_numeric_string(b) { + match (a.trim().parse::<i64>(), b.trim().parse::<i64>()) { + (Ok(na), Ok(nb)) => na.cmp(&nb), + _ => { + let na: f64 = a.trim().parse().unwrap_or(0.0); + let nb: f64 = b.trim().parse().unwrap_or(0.0); + na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal) + } + } + } else { + a.cmp(b) + } +} + pub fn instance_of<T>(_value: &PhpMixed) -> bool { // TODO(phase-d): PHP `instanceof` needs the runtime class of the value, which PhpMixed::Object // does not carry. |
