I have this talk about the MoneyPHP library and handling currencies in general. I often get a question: is it possible to define custom currencies in MoneyPHP? A possible use case would be loyalty cards for example, with their own scoring system.
The answer is: YES! MoneyPHP not only accepts all ISO 4217 currencies, but also allows you to define a custom list with any identifiers and any subunits. So for example, you can create a currency called
BlahBlahBlah that has 7 decimal points and then convert an amount from your currency to another by providing a custom exchange list.
In the example below, we’re creating two fictious currencies:
TheirPoints. The first one has two decimal points while another has none (only whole numbers). We create a
Money object that holds exactly 123.45 MyPoints.
Then we specify an exchange table which says that 1 TheirPoint equals 0.5 MyPoints. We create a currency converter and then try to convert one currency to another.
123.45 * 0.5 equals
61.725. But since TheirPoints subunit is 0, the amount has to be rounded up, so we get
use Money\Converter; use Money\Currencies\CurrencyList; use Money\Currency; use Money\Exchange\FixedExchange; use Money\Money; $currencyList = new CurrencyList([ 'MyPoints' => 2, 'TheirPoints' => 0, ]); $myCardAmount = new Money(12345, new Currency('MyPoints')); $exchange = new FixedExchange([ 'MyPoints' => [ 'TheirPoints' => 0.5, ], ]); $converter = new Converter($currencyList, $exchange); $theirCardAmount = $converter->convert( $myCardAmount, new Currency('TheirPoints') ); echo $theirCardAmount->getAmount(); // 62
However, there can be a problem with currency formatting using PHP’s
intl library. Consider this example:
$numberFormatter = new \NumberFormatter( 'en_US', \NumberFormatter::CURRENCY ); $moneyFormatter = new \Money\Formatter\IntlMoneyFormatter( $numberFormatter, $currencyList ); echo $moneyFormatter->format($theirCardAmount); // The 62.00
The odd output (
The instead of
Their and the unwanted fractional part) comes from the fact that PHP’s
NumberFormatter handles only ISO currencies which have 3-character symbols. So you should use
\NumberFormatter::DECIMAL instead and add your custom symbol manualy.