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: MyPoints and 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 62.

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.

See my post about handling money in PHP