Context
Surfaced during the pre-merge audit of #4. Low-risk edge case, deferred to 0.6.1.
Problem
In Amount::fromToman() and Amount::add(), the arithmetic $t * 10 (and $a->rials + $b->rials) can silently promote to float once the result exceeds PHP_INT_MAX. On 64-bit PHP that's ~9.22e18 rials, or ~9.22e17 toman (~92 quadrillion toman, ~$2T USD at current rates) — absurd in IRR terms, but the Amount VO's design philosophy is refuse ambiguous values early, not work most of the time.
The float slips past the int $rials constructor because PHP silently casts it back, producing a value with lost precision and no error raised.
Repro
// 64-bit PHP
$huge = intdiv(PHP_INT_MAX, 10) + 1; // first toman value that overflows *10
Amount::fromToman($huge); // silently wrong — no FormatException
Proposed fix
- Guard
fromToman(): if $t > intdiv(PHP_INT_MAX, 10) → throw FormatException with a new ErrorCode::AMOUNT_OVERFLOW (or reuse AMOUNT_NEGATIVE's sibling — preference: new code, it's a distinct invariant).
- Guard
add(): if $a->rials > PHP_INT_MAX - $b->rials → same throw.
- Regression tests covering both boundaries (at-limit = ok, one-past-limit = throws).
Why 0.6.1, not a 0.6 blocker
Practical IRR amounts won't hit this in a hundred lifetimes. But the fix is cheap, the invariant is the VO's whole point, and untested overflow paths rot. Ship it as a patch.
Context
Surfaced during the pre-merge audit of #4. Low-risk edge case, deferred to 0.6.1.
Problem
In
Amount::fromToman()andAmount::add(), the arithmetic$t * 10(and$a->rials + $b->rials) can silently promote to float once the result exceedsPHP_INT_MAX. On 64-bit PHP that's ~9.22e18rials, or ~9.22e17toman (~92 quadrillion toman, ~$2T USD at current rates) — absurd in IRR terms, but theAmountVO's design philosophy is refuse ambiguous values early, not work most of the time.The float slips past the
int $rialsconstructor because PHP silently casts it back, producing a value with lost precision and no error raised.Repro
Proposed fix
fromToman(): if$t > intdiv(PHP_INT_MAX, 10)→ throwFormatExceptionwith a newErrorCode::AMOUNT_OVERFLOW(or reuseAMOUNT_NEGATIVE's sibling — preference: new code, it's a distinct invariant).add(): if$a->rials > PHP_INT_MAX - $b->rials→ same throw.Why 0.6.1, not a 0.6 blocker
Practical IRR amounts won't hit this in a hundred lifetimes. But the fix is cheap, the invariant is the VO's whole point, and untested overflow paths rot. Ship it as a patch.