number_format() & money_format()
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). No detection_hints.tools specified; PHPCS/Psalm won't flag missing formatting or non-locale-aware usage. money_format() removal in PHP 8 would be caught by a deprecation/compat scanner (e.g. PHPCompatibility) which is more like d5, but the broader misconception (locale-awareness) is only spotted in review or QA.
Closest to 'simple parameterised fix' (e3). Per quick_fix, swap echo $price for number_format($price, 2), or replace money_format() with NumberFormatter::formatCurrency(). It's slightly more than a one-line swap because money_format replacement requires constructing a NumberFormatter with a locale.
Closest to 'localised tax' (b3). Formatting concerns live in the presentation layer; choosing number_format over NumberFormatter doesn't ripple through the architecture, though it does recur across many display sites.
Closest to 'serious trap' (t7). The misconception is explicit: developers assume number_format() respects locale (as similar functions in other languages do), but it doesn't. Combined with money_format() being removed in PHP 8 and silent string-to-float coercion losing precision, the 'obvious' usage contradicts expectations from other ecosystems.
Also Known As
TL;DR
Explanation
number_format(float $num, int $decimals, string $decimal_separator, string $thousands_separator) converts a float to a formatted string. The defaults produce English-style formatting (1,234.56), but all separators are configurable for locale-specific output (1.234,56 for German). money_format() was a thin wrapper around the C library strfmon() — locale-dependent, not available on Windows, and removed in PHP 8.0. The modern replacement is NumberFormatter from the Intl extension, which handles currency symbols, locale rules, and sign conventions correctly. For simple price display without Intl, number_format() with explicit separators is safe and predictable.
Common Misconception
Why It Matters
Common Mistakes
- Using money_format() in code targeting PHP 8+ — it was removed; replace with NumberFormatter::formatCurrency() or number_format() with explicit separators.
- Storing number_format() output in a database — it adds commas and periods that break numeric operations; always store raw numbers, format only for display.
- Using number_format() for locale-sensitive output without configuring separators — the defaults are English-style regardless of server locale.
- Passing a string to number_format() — it silently coerces to float, which can lose precision for large integers; use intl NumberFormatter for arbitrary precision.
- Formatting floats for financial calculations — floats have precision errors; use bcmath or integer cents and format only at display time.
Code Examples
<?php
// ❌ Raw float output — no thousands separator, inconsistent decimals
$price = 1234.5;
echo $price; // '1234.5' — missing separator, missing trailing zero
echo round($price, 2); // '1234.5' — still no formatting
// ❌ money_format() — removed in PHP 8.0
echo money_format('%.2n', $price); // Fatal error on PHP 8+
<?php
// ✅ number_format() — simple, predictable
$price = 1234.5;
echo number_format($price, 2); // '1,234.50'
echo number_format($price, 2, ',', '.'); // '1.234,50' — German style
echo '$' . number_format($price, 2); // '$1,234.50'
// ✅ NumberFormatter (Intl) — locale-correct currency
$fmt = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency(1234.5, 'USD'); // '$1,234.50'
$fmt_de = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
echo $fmt_de->formatCurrency(1234.5, 'EUR'); // '1.234,50 €'