You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
genRound sends the third argument of round() through convertIntExpr.
Since PHP 8.4 that parameter is a RoundingMode enum, and php::fn::round()
declares the mode as an Int, so the enum is put through an object-to-int
conversion.
This repo converts that argument to an int. With the phpx side fixed, the
compiled binary stops crashing but still disagrees with PHP:
Warning: Object of class RoundingMode could not be converted to int
3
The conversion yields 1, which is PHP_ROUND_HALF_UP, so banker's rounding
silently becomes half away from zero.
Measured on a compiled binary, PHP 8.5.4 ZTS + embed, GCC 15, Linux x64:
phpx
genRound
round(2.5, 0, RoundingMode::HalfEven)
master
master
stack smashing abort
fixed
master
warning, then 3
master
fixed
clean TypeError, no memory corruption
fixed
fixed
2, same as PHP
Code that spells out HalfEven is usually money code, where half-up is the
exact behaviour it was avoiding.
Suggested fix
Let a mode that is not statically an int fall through to the dynamic path, which
passes the enum to the runtime function unchanged. The legacy PHP_ROUND_*
integer constants keep the native call. PR follows.
Summary
genRoundsends the third argument ofround()throughconvertIntExpr.Since PHP 8.4 that parameter is a
RoundingModeenum, andphp::fn::round()declares the mode as an
Int, so the enum is put through an object-to-intconversion.
Reproducer
Expected (PHP 8.5.4)
Actual (compiled binary)
What is actually going on
Two separate problems stack on this one line, and they are worth separating:
phpx returns an unevaluated class constant for an enum case of an
internal class, so
RoundingMode::HalfEvenis not a usable value at all.That is the crash. Reported as constant(zend_class_entry*, name) returns an unevaluated class constant phpx#97, with a fix in fix: evaluate class constants before returning them phpx#98.
This repo converts that argument to an int. With the phpx side fixed, the
compiled binary stops crashing but still disagrees with PHP:
The conversion yields 1, which is
PHP_ROUND_HALF_UP, so banker's roundingsilently becomes half away from zero.
Measured on a compiled binary, PHP 8.5.4 ZTS + embed, GCC 15, Linux x64:
round(2.5, 0, RoundingMode::HalfEven)32, same as PHPCode that spells out
HalfEvenis usually money code, where half-up is theexact behaviour it was avoiding.
Suggested fix
Let a mode that is not statically an int fall through to the dynamic path, which
passes the enum to the runtime function unchanged. The legacy
PHP_ROUND_*integer constants keep the native call. PR follows.