what happened in PHP8.0.0 to break usort(...(int)(strlen($a)<strlen($b)));?
Asked Answered
V

3

14

the code

<?php
$consts = get_defined_constants();
$consts = array_keys($consts);
usort($consts,function($a,$b){return (int)(strlen($a)<strlen($b));});
foreach($consts as $const){
    echo strlen($const).": ".$const."\n";
}

will, prior to PHP 8.0.0, print all defined constants from longest to shortest, as i expected. 7.3.13 starts with

62: SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE
62: SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE
60: SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE
60: SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE
51: SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX

but i have no idea what PHP8.0.0 did, it's output starts with:

9: E_WARNING
21: FILTER_FLAG_STRIP_LOW
7: E_ERROR
26: FILTER_FLAG_STRIP_BACKTICK

you can see it yourself on 3v4l: https://3v4l.org/MP2IF

so what happened in PHP 8.0.0 to break this code?

Vituperation answered 20/12, 2020 at 17:33 Comment(0)
O
20

Most of the other answers here are focused around how to fix the problem, but I thought I'd try and explain why this changed in PHP 8, which I think is what you're interested in.

PHP 8 introduced the Stable Sorting RFC, which (as it sounds) means that all sorting functions in PHP are now "stable". More details about this in the link.

The other answers covered this well already, but your function returns either zero or a number greater than zero. Previous implementations of sorting in PHP (in all versions lower than 8) considered zero and a negative number the same; as the RFC above mentions, the check was simply for a number greater than zero, or not. Returning zero would mean that these elements were treated the same as the case where $a < $b.

PHP introduced a deprecation warning so that a lot of sorting implementations that return booleans would still work. The RFC gives some more details on this, but importantly it means PHP 8 is still backwards compatible with them (hence this being a deprecation notice, rather than a warning). The edge-case here is that while your function effectively returns a boolean - 0 for the same length, and 1 for the case where $a < $b - because you cast this to an integer, the backwards-compatibility check in PHP 8 does not catch it, and so all "equal" elements are considered as though $a < $b

Compare:

function($a, $b) { return (int) (strlen($a) < strlen($b)); }

As in the question - works correctly in PHP <8, but raises no deprecation notice. https://3v4l.org/MP2IF


function($a, $b) { return strlen($a) < strlen($b); }

Returns a boolean, so the backwards-compatibility check in PHP 8 works correctly. But a deprecation notice is now raised. https://3v4l.org/fWR2Y


function($a, $b) { return strlen($b) <=> strlen($a); }

The "correct" solution, working correctly in all versions (at least since the spaceship operator was introduced). https://3v4l.org/6XRYW

Ovenbird answered 20/12, 2020 at 18:57 Comment(1)
because you cast this to an integer, the backwards-compatibility check in PHP 8 does not catch it - funnily enough, i cast it to integer to get rid of the warning in some old PHP5-7 code I was updating to PHP8 xDVituperation
D
15

It's because you are returning either 0 or 1, not -1. You should use the spaceship operator instead: https://3v4l.org/PLYAP

usort($consts, function($a, $b) {
    return strlen($b) <=> strlen($a);
});

0 should be returned in case both sides are equal.

Dolphin answered 20/12, 2020 at 17:55 Comment(0)
A
5

I don't know what changed internally to cause this difference, but your sorting callback is a bit funky. It should only be returning 0 if $a and $b are functionally "equal" (in this case, if they have the same string lengths). Otherwise it should return 1 if $a should sort before $b, or -1 otherwise. If I tweak your callback appropriately, I get expected output.

usort($consts, function($a, $b) {
    $aLen = strlen($a);
    $bLen = strlen($b);
    if ($aLen === $bLen) {
        return 0;
    }
    return $aLen < $bLen ? 1 : -1;
});

3v4l: https://3v4l.org/UScFO

Alarice answered 20/12, 2020 at 17:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.