PHP8.0.0 中发生了什么破坏了 usort(...(int)(strlen($a)<strlen($b)));?
what happened in PHP8.0.0 to break usort(...(int)(strlen($a)<strlen($b)));?
代码
<?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";
}
将在 PHP 8.0.0 之前,按照我的预期从最长到最短打印所有定义的常量。 7.3.13 开始于
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
但我不知道 PHP8.0.0 做了什么,它的输出以:
开头
9: E_WARNING
21: FILTER_FLAG_STRIP_LOW
7: E_ERROR
26: FILTER_FLAG_STRIP_BACKTICK
你可以在3v4l上自己看:https://3v4l.org/MP2IF
所以 PHP 8.0.0 中发生了什么来破解此代码?
我不知道是什么内部变化导致了这种差异,但您的排序回调有点古怪。如果 $a 和 $b 在功能上“相等”(在这种情况下,如果它们具有相同的字符串长度),它应该只 returning 0 。否则它应该 return 1 如果 $a 应该排在 $b 之前,或者 -1 否则。如果我适当地调整你的回调,我会得到预期的输出。
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
这是因为您要返回 0 或 1,而不是 -1。
您应该改用宇宙飞船运算符:
https://3v4l.org/PLYAP
usort($consts, function($a, $b) {
return strlen($b) <=> strlen($a);
});
如果两边相等,则应返回0。
这里的大多数其他答案都集中在如何解决问题上,但我想我会尝试解释 为什么 这在 PHP 8 中发生了变化,我认为这是你感兴趣的。
PHP 8 引入了 Stable Sorting RFC,这(听起来)意味着 PHP 中的所有排序函数现在都“稳定”了。 link.
中有关此的更多详细信息
其他答案已经很好地涵盖了这一点,但是您的函数 return 要么是零,要么是一个大于零的数字。 PHP(在低于 8 的所有版本中)中的先前排序实现将零和负数视为相同;如上面的 RFC 所述,检查只是针对大于零或不大于零的数字。返回零意味着这些元素的处理方式与 $a < $b
.
的情况相同
PHP 引入了弃用警告,因此许多 return 布尔值的排序实现仍然有效。 RFC 对此提供了更多详细信息,但重要的是,它意味着 PHP 8 仍然向后兼容它们(因此这是弃用通知,而不是警告)。这里的边缘情况是,虽然你的函数有效地 returns 一个布尔值 - 相同长度为 0,而 $a < $b
的情况为 1 - 因为你将其转换为整数,向后兼容性检查in PHP 8 没有捕捉到它,因此所有“相等”元素都被视为 $a < $b
比较:
function($a, $b) { return (int) (strlen($a) < strlen($b)); }
如问题所示 - 在 PHP <8 中正常工作,但不会引发弃用通知。 https://3v4l.org/MP2IF
function($a, $b) { return strlen($a) < strlen($b); }
Returns 一个布尔值,因此 PHP 8 中的向后兼容性检查工作正常。但是现在提出了弃用通知。 https://3v4l.org/fWR2Y
function($a, $b) { return strlen($b) <=> strlen($a); }
“正确”的解决方案,在所有版本中都能正常工作(至少自从引入宇宙飞船操作员以来)。 https://3v4l.org/6XRYW
代码
<?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";
}
将在 PHP 8.0.0 之前,按照我的预期从最长到最短打印所有定义的常量。 7.3.13 开始于
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
但我不知道 PHP8.0.0 做了什么,它的输出以:
开头9: E_WARNING
21: FILTER_FLAG_STRIP_LOW
7: E_ERROR
26: FILTER_FLAG_STRIP_BACKTICK
你可以在3v4l上自己看:https://3v4l.org/MP2IF
所以 PHP 8.0.0 中发生了什么来破解此代码?
我不知道是什么内部变化导致了这种差异,但您的排序回调有点古怪。如果 $a 和 $b 在功能上“相等”(在这种情况下,如果它们具有相同的字符串长度),它应该只 returning 0 。否则它应该 return 1 如果 $a 应该排在 $b 之前,或者 -1 否则。如果我适当地调整你的回调,我会得到预期的输出。
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
这是因为您要返回 0 或 1,而不是 -1。 您应该改用宇宙飞船运算符: https://3v4l.org/PLYAP
usort($consts, function($a, $b) {
return strlen($b) <=> strlen($a);
});
如果两边相等,则应返回0。
这里的大多数其他答案都集中在如何解决问题上,但我想我会尝试解释 为什么 这在 PHP 8 中发生了变化,我认为这是你感兴趣的。
PHP 8 引入了 Stable Sorting RFC,这(听起来)意味着 PHP 中的所有排序函数现在都“稳定”了。 link.
中有关此的更多详细信息其他答案已经很好地涵盖了这一点,但是您的函数 return 要么是零,要么是一个大于零的数字。 PHP(在低于 8 的所有版本中)中的先前排序实现将零和负数视为相同;如上面的 RFC 所述,检查只是针对大于零或不大于零的数字。返回零意味着这些元素的处理方式与 $a < $b
.
PHP 引入了弃用警告,因此许多 return 布尔值的排序实现仍然有效。 RFC 对此提供了更多详细信息,但重要的是,它意味着 PHP 8 仍然向后兼容它们(因此这是弃用通知,而不是警告)。这里的边缘情况是,虽然你的函数有效地 returns 一个布尔值 - 相同长度为 0,而 $a < $b
的情况为 1 - 因为你将其转换为整数,向后兼容性检查in PHP 8 没有捕捉到它,因此所有“相等”元素都被视为 $a < $b
比较:
function($a, $b) { return (int) (strlen($a) < strlen($b)); }
如问题所示 - 在 PHP <8 中正常工作,但不会引发弃用通知。 https://3v4l.org/MP2IF
function($a, $b) { return strlen($a) < strlen($b); }
Returns 一个布尔值,因此 PHP 8 中的向后兼容性检查工作正常。但是现在提出了弃用通知。 https://3v4l.org/fWR2Y
function($a, $b) { return strlen($b) <=> strlen($a); }
“正确”的解决方案,在所有版本中都能正常工作(至少自从引入宇宙飞船操作员以来)。 https://3v4l.org/6XRYW