Brick/Money is a money and currency library for PHP that handles exact calculations on monies of any size. When you are working with financial data then it is a serious matter, and small rounding mistakes in an application may lead to serious consequences in real life. That’s why floating-point arithmetic is not suited for monetary calculations. So we can use this library that is based on brick/math and handles exact calculations on monies of any size.
Installation
This library is installable via Composer:
composer require brick/money
Requirements
This library requires PHP 7.4 or later.
Creating a Money
To create Money, call the of() factory method:
use Brick\Money\Money;
$money = Money::of(50, 'USD'); // USD 50.00
$money = Money::of('19.9', 'USD'); // USD 19.90
Alternatively, you can create Money from a number of “minor units” (cents), using the ofMinor() method:
use Brick\Money\Money;
$money = Money::ofMinor(1234, 'USD'); // USD 12.34
Basic operations
Money is an immutable class: its value never changes, so it can be safely passed around. All operations on a Money, therefore, return a new instance:
use Brick\Money\Money;
$money = Money::of(50, 'USD');
echo $money->plus('4.99'); // USD 54.99
echo $money->minus(1); // USD 49.00
echo $money->multipliedBy('1.999'); // USD 99.95
echo $money->dividedBy(4); // USD 12.50
You can add and subtract Money instances as well:
use Brick\Money\Money;
$cost = Money::of(25, 'USD');
$shipping = Money::of('4.99', 'USD');
$discount = Money::of('2.50', 'USD');
echo $cost->plus($shipping)->minus($discount); // USD 27.49
If the two Money instances are not of the same currency, an exception is thrown:
use Brick\Money\Money;
$a = Money::of(1, 'USD');
$b = Money::of(1, 'EUR');
$a->plus($b); // MoneyMismatchException
If the result needs rounding, a rounding mode must be passed as a second parameter, or an exception is thrown:
use Brick\Money\Money;
use Brick\Math\RoundingMode;
$money = Money::of(50, 'USD');
$money->plus('0.999'); // RoundingNecessaryException
$money->plus('0.999', RoundingMode::DOWN); // USD 50.99
$money->minus('0.999'); // RoundingNecessaryException
$money->minus('0.999', RoundingMode::UP); // USD 49.01
$money->multipliedBy('1.2345'); // RoundingNecessaryException
$money->multipliedBy('1.2345', RoundingMode::DOWN); // USD 61.72
$money->dividedBy(3); // RoundingNecessaryException
$money->dividedBy(3, RoundingMode::UP); // USD 16.67
Cash rounding
Some currencies do not allow the same increments for cash and cashless payments. For example, CHF (Swiss Franc) has 2 fraction digits and allows increments of 0.01 CHF, but Switzerland does not have coins of less than 5 cents, or 0.05 CHF.
You can deal with such monies using CashContext:
use Brick\Money\Money;
use Brick\Money\Context\CashContext;
use Brick\Math\RoundingMode;
$money = Money::of(10, 'CHF', new CashContext(5)); // CHF 10.00
$money->dividedBy(3, RoundingMode::DOWN); // CHF 3.30
$money->dividedBy(3, RoundingMode::UP); // CHF 3.35
Custom scale
You can use custom scale monies by providing a CustomContext:
use Brick\Money\Money;
use Brick\Money\Context\CustomContext;
use Brick\Math\RoundingMode;
$money = Money::of(10, 'USD', new CustomContext(4)); // USD 10.0000
$money->dividedBy(7, RoundingMode::UP); // USD 1.4286
Auto scale
If you need monies that adjust their scale to fit the operation result, then AutoContext is for you:
use Brick\Money\Money;
use Brick\Money\Context\AutoContext;
$money = Money::of('1.10', 'USD', new AutoContext()); // USD 1.1
$money->multipliedBy('2.5'); // USD 2.75
$money->dividedBy(8); // USD 0.1375
Advanced calculations
You may occasionally need to chain several operations on a Money, and only apply a rounding mode on the very last step; if you applied a rounding mode on every single operation, you might end up with a different result. This is where RationalMoney comes into play. This class internally stores the amount as a rational number (a fraction). You can create a RationalMoney from a Money, and conversely:
use Brick\Money\Money;
use Brick\Math\RoundingMode;
$money = Money::of('9.5', 'EUR') // EUR 9.50
->toRational() // EUR 950/100
->dividedBy(3) // EUR 950/300
->plus('17.795') // EUR 6288500/300000
->multipliedBy('1.196') // EUR 7521046000/300000000
->to($money->getContext(), RoundingMode::DOWN) // EUR 25.07
This package has a lot of other features and options, If you wanna dig more, please visit Github.