brick/money
Immutable money & currency library for PHP with exact arithmetic, explicit rounding control, and support for any-sized amounts. Built on brick/math to avoid floating-point errors; works with ISO currencies and integrates well with GMP/BCMath for speed.
💥 Breaking changes
MoneyBag can no longer be instantiated with new: its constructor is now private; use MoneyBag::zero() to create an empty instanceMoney::convertedTo() now defaults to DefaultContext instead of $this's contextnull for the $context parameter:
CurrencyConverter::convert()Money::of()Money::ofMinor()Money::zero()Money::convertedTo()Money::allocate() signature has changed: replace allocate(1, 2, 3) with allocate([1, 2, 3], AllocationMode::FloorToFirst) to keep the same behaviour as beforeMoney::allocateWithRemainder() has been removed: replace allocateWithRemainder(1, 2, 3) with allocate([1, 2, 3], AllocationMode::BlockSeparate) to keep the same behaviour as beforeMoney::split() signature has changed: replace split(3) with split(3, SplitMode::ToFirst) to keep the same behaviour as beforeMoney::splitWithRemainder() has been removed: replace splitWithRemainder(3) with split(3, SplitMode::Separate) to keep the same behaviour as beforeMoney with AutoContext:
quotient()remainder()quotientAndRemainder()allocate()split()MoneyBag no longer retains zero-balance currencies after plus()/minus() operationsExchangeRateProvider::getExchangeRate() signature has changed: it now accepts Currency instances, and returns BigNumber|nullCurrencyConversionException has been renamed to ExchangeRateException, with ExchangeRateProviderException and ExchangeRateNotFoundException subclassesPdoProvider::setParameters() has been removed, use dimension bindings insteadPdoProvider constructor is now private, use PdoProvider::builder()->...->build() to create an instancePdoProviderConfiguration has been removed, use PdoProvider::builder() insteadCurrencyConverter::convert() signature has changed: parameter $dimensions now comes before $contextCachedProvider::invalidate() has been removed, pass a PSR-16 cache implementation to the constructor if you need to invalidate the cacheMoneyComparator constructor now requires a ComparisonMode instance, explicitly pass an instance of PairwiseMode to keep the same behaviour as beforeConfigurableProvider is now immutable: setExchangeRate() has been removed, use ConfigurableProvider::builder()->addExchangeRate()->build() insteadMoney::convertedTo()RationalMoney::convertedTo()CurrencyConverter::convert()CurrencyConverter::convertToRational()1:
Money::convertedTo()RationalMoney::convertedTo()[@internal](https://github.com/internal)Context methods are now [@internal](https://github.com/internal): userland code should only rely on constructorsContext, Monetary, and MoneyException are now sealed: userland implementations are no longer supportedExchangeRateProvider implementations now consistently return 1 for same-currency pairsDeprecated methods removed:
AbstractMoney::to() has been removed, use toContext() insteadAbstractMoney::isAmountAndCurrencyEqualTo() has been removed, use isSameValueAs() insteadMoney::total() has been removed, use sum() insteadMoney::getUnscaledAmount() has been removed, use getAmount()->getUnscaledValue() insteadRationalMoney::simplified() has been removed, RationalMoney is always in its simplest form nowMoneyBag::add() has been removed, use plus() instead, which returns a new instanceMoneyBag::subtract() has been removed, use minus() instead, which returns a new instanceThe following breaking changes only affect you if you're using named arguments:
Currency::ofNumericCode() now uses $numericCode as the parameter nameIsoCurrencyProvider::getCurrencyByNumericCode() now uses $numericCode as the parameter nameMoneyBag::plus() and minus() now use $that as the parameter name⚠️ Deprecations
MoneyBag::fromMonies() is deprecated, use of() insteadProviderChain is deprecated, use ChainProvider instead✨ New features
ExchangeRateProvider, CurrencyConverter and MoneyComparatorMoney, RationalMoney, and MoneyBag in MoneyComparatorMoneyComparatorMoney::allocate() API with five algorithms, exposed through the new AllocationMode enum:
FloorToFirst (this is the implementation allocate() used previously)FloorToLargestRatioFloorToLargestRemainderFloorSeparateBlockSeparate (this is the implementation allocateWithRemainder() used previously)Money::split() API with two algorithms, exposed through the new SplitMode enum:
ToFirst (this is the implementation split() used previously)Separate (this is the implementation splitWithRemainder() used previously)MoneyMismatchException now has explicit CurrencyMismatchException and ContextMismatchException subclassesMoneyFormatException, thrown by MoneyFormatter::format()MoneyBag: isZero(), multipliedBy(), dividedBy(), negated(), isEqualTo(), of() (replaces fromMonies())RationalMoney: min(), max(), sum()CachedProviderChainProvider (replaces ProviderChain)PdoProvider⚠️ Deprecations
AbstractMoney::isAmountAndCurrencyEqualTo() is deprecated, use isSameValueAs() instead🔄 Reverted deprecations
AbstractMoney::isEqualTo() with a money in a different currency (introduced in 0.12.1) has been removed; this method will continue to throw a MoneyMismatchException✨ New features
AbstractMoney::isSameValueAs() (replaces isAmountAndCurrencyEqualTo())✨ New features
Money::allocate() & allocateWithRemainder() now accept BigNumber|int|string and support decimal & rational ratiosMoney::remainder()abs() and negated() are now available on AbstractMoney📌 Compatibility
brick/math version 0.17📌 Compatibility
brick/math version 0.16💥 Breaking changes
(string) $float to get the same behaviour as before (brick/math#105):
Money::of(), ofMinor(), plus(), minus(), multipliedBy(), dividedBy(), quotient(), quotientAndRemainder(), convertedTo()RationalMoney::of(), plus(), minus(), multipliedBy(), dividedBy()AbstractMoney::compareTo(), isEqualTo(), isLessThan(), isLessThanOrEqualTo(), isGreaterThan(), isGreaterThanOrEqualTo()ConfigurableProvider::setExchangeRate()Currency::of() with a numeric code is no longer supported, use Currency::ofNumericCode() instead (#104)Currency instance from Currency::ofNumericCode() instead (#104):
Money::of()Money::ofMinor()Money::zero()Money::convertedTo()RationalMoney::of()CurrencyConverter::convert()CurrencyConverter::convertToRational()IsoCurrencyProvider::getCurrency()RationalMoney is now always simplified to lowest terms: USD 25/100 is automatically simplified to USD 1/4Currency::$numericCode is now nullablePdoProviderConfiguration now has a private constructor, use a factory method insteadMoney::create() is now protectedClass name case changes:
ISOCurrencyProvider has been renamed to IsoCurrencyProviderPDOProvider has been renamed to PdoProviderPDOProviderConfiguration has been renamed to PdoProviderConfigurationDeprecated methods removed:
Currency::is() has been removed, use Currency::isEqualTo() insteadMoneyBag::getAmount() has been removed, use MoneyBag::getMoney()->getAmount() insteadMoney::formatTo() has been removed, use Money::formatToLocale() insteadMoney::formatWith() has been removed, use MoneyNumberFormatter::format() instead⚠️ Deprecations
RationalMoney::simplified() is deprecated, as it is now a no-opMoney::getUnscaledAmount() is deprecated, use getAmount()->getUnscaledValue() insteadMoney::total() is deprecated, use sum() insteadAbstractMoney::to() is deprecated, use toContext() insteadnull to the $context parameter of the following methods is deprecated, use named arguments if you need to skip $context:
CurrencyConverter::convert()Money::of()Money::ofMinor()Money::zero()null to the $context parameter of Money::convertedTo() is deprecated, use an explicit Context instance; the default will change to DefaultContext in a future versionMoneyBag with new is deprecated, use MoneyBag::zero() or MoneyBag::fromMonies() insteadMoneyBag::add() is deprecated, use plus() instead, which returns a new instanceMoneyBag::subtract() is deprecated, use minus() instead, which returns a new instance📌 Compatibility
brick/math:~0.15👌 Improvements
InvalidArgumentException thrown now implement MoneyExceptionCashContext::applyTo() now performs step ⟷ scale validation✨ New features
AbstractMoney::toRational()Money::sum() (replaces total())RationalMoney::convertedTo()RationalMoney::abs()RationalMoney::negated()MoneyBag immutable API:
MoneyBag::zero()MoneyBag::fromMonies()MoneyBag::plus()MoneyBag::minus()ContextException thrown when a context cannot be appliedPDOProviderConfiguration factory methods:
forCurrencyPair()forFixedSourceCurrency()forFixedTargetCurrency()MoneyBag now implements JsonSerializable⚠️ Deprecations
Money::create() is now marked as [@internal](https://github.com/internal), and will be made protected in version 0.12⚠️ Deprecations
trigger_deprecation() calls to methods that were already marked as [@deprecated](https://github.com/deprecated), so deprecation notices are now emitted at runtime📌 Compatibility
brick/math:~0.14.4👌 Improvements
💥 Breaking changes
final:
CurrencyConversionExceptionMoneyMismatchExceptionUnknownCurrencyExceptionCustomContext now validates the step and will throw an exception if an invalid step is givenMoneyContainer has been removed (replaced with Monetary)AbstractMoney::getAmounts() has been removed (replaced with getMonies())MoneyBag::getAmounts() has been removed (replaced with getMonies())CurrencyConverter::convert() and convertToRational() now accept a Monetary instance (which still includes Money, RationalMoney and MoneyBag)MoneyBag::add() and subtract() now accept a Monetary instance (which still includes Money, RationalMoney and MoneyBag)⚠️ Deprecations
Currency::of() with a numeric code is deprecated, use Currency::ofNumericCode() insteadISOCurrencyProvider::getCurrency() with a numeric code is deprecated, use getCurrencyByNumericCode() insteadCurrencyConverter::convert() or convertToRational() with a numeric currency code is deprecated, use a Currency instance insteadMoney::of(), ofMinor(), zero() or convertedTo() with a numeric currency code is deprecated, use a Currency instance insteadRationalMoney::of() with a numeric currency code is deprecated, use a Currency instance insteadMoneyBag::getAmount() is deprecated, use getMoney() insteadMoney::formatTo() is deprecated, use Money::formatToLocale() insteadMoney::formatWith() is deprecated, use MoneyNumberFormatter::format() insteadCurrency::is() is deprecated, use Currency::isEqualTo() instead[!IMPORTANT] The convenience of passing a currency by ISO numeric code in addition to alphabetic code has been deprecated, leaving only alphabetic-code lookup in generic APIs. For example,
Money::of()will acceptCurrency|stringin the future, instead ofCurrency|string|inttoday. This makes explicit the separation between retrieval by alphabetic code, which has strong backwards compatibility guarantees, and retrieval by numeric code, which may change in minor versions due to ISO reassignments. This will require users to explicitly obtain a currency throughCurrency::ofNumericCode(), which is documented as not being covered by the same BC guarantees.
✨ New features
Money::of(), Currency::of(), etc. (#104 by @survik1)CurrencyTypeCurrency::getCurrencyType() returns the type of the currencyCurrency::ofNumericCode() returns a currency by its numeric ISO 4217 codeCurrency::isEqualTo() compares two currencies for equality (replaces is())ISOCurrencyProvider::getCurrencyByNumericCode() returns a currency by its numeric codeISOCurrencyProvider::getHistoricalCurrenciesForCountry() returns historical currencies for a countryMoneyBag::getMoney() returns the contained amount in a given currency (replaces getAmount())MoneyBag::getMonies() returns the contained monies (replaces getAmounts())Money::formatToLocale() formats the amount to a locale (replaces formatTo()) (#105 by @mklepaczewski)RationalMoney::zero() returns a zero RationalMoney in a given currencyMonetary (replaces MoneyContainer)MoneyFormatter formats a given Money object (#105 by @mklepaczewski)MoneyLocaleFormatter formats a given Money object to a locale (#105 by @mklepaczewski)MoneyNumberFormatter formats a given Money object using a NumberFormatter instance (#105 by @mklepaczewski)👌 Improvements
brick/math version 0.14 (#101 by @markwalet)✨ New features
CurrencyConversionException (#99 by @arokettu)👌 Improvements
brick/math version 0.13 (#96 by @ekvedaras)💥 ISO currency changes
ZWG (Zimbabwe Gold) has been addedZWL (Zimbabwean Dollar) has been removedSLL (Sierra Leonean Leone) has been removedZW) has been changed to ZWG (Zimbabwe Gold)SLL currency has been removed from Sierra Leone (SL), which only has SLE now💥 Breaking changes
PDOProviderConfiguration no longer has getters, its properties are public readonlyRoundingMode from brick/math is now an enum, so:
int rounding mode now accept a RoundingMode instance insteadRoundingMode::UP💥 Breaking changes
HRK currency (Kuna) has been removed from the ISO currency providerHR country (Croatia) is now mapped to EUR (Euro)PDOProviderConfiguration now has a proper constructor, and its properties are no longer publicPDOProviderConfiguration now throws exceptions in the constructor when configuration is invalidExchangeRateProvider implementation, you will need to update your getExchangeRate() method signatureStringable objects to of() or any of the methods internally calling of(), and have strict_types enabled, you will need to explicitly cast these objects to string first👌 Improvements
brick/math version 0.11💥 Breaking changes
AbstractMoney is now officially sealed, extending it yourself is not supported✨ New features
Money and RationalMoney now implement JsonSerializable💥 Breaking changes
AbstractMoney::getAmount() now has a return typeCurrencyConverter's constructor does not accept a default $context anymoreCurrencyConverter::convert() now requires the $context previously accepted by the constructor as third parameterMoney::allocateWithRemainder() now refuses to allocate a portion of the amount that cannot be spread over all ratios, and instead adds that amount to the remainder (#55)Money::splitWithRemainder() now behaves like allocateWithRemainder()✨ New ISO currencies
SLE (Leone) in Sierra Leone (SL)👌 Improvements
brick/math version 0.10✨ Add support for VED (Venezuelan bolívar) currency.
✨ New methods
Money::splitWithRemainder()Money::allocateWithRemainder()These methods perform like their split() and allocate() counterparts, but append the remainder at the end of the returned array instead of spreading it over the first monies.
Thanks @NCatalani!
👌 Improvement
BaseCurrencyProvider now always returns a BigNumber for convenience (#37).
This is useful if you're using BaseCurrencyProvider on its own, not just in CurrencyConverter.
Thanks @rdarcy1!
👌 Improvements
brick/math version 0.9⚠️ Caution
When using brick/math version 0.9, the Money factory methods such as of() and ofMinor() now accept decimal numbers in the form .123 and 123., and do not throw an exception anymore in this case.
🐛 Bug fix
MoneyBag::getAmount(), add() and subtract() would throw an exception when using a custom currency (#25).
✨ New method
AbstractMoney::isAmountAndCurrencyEqualTo() compares a money to another. (#17)
This method is different from isEqualTo() in 2 aspects:
false if the money is in another currency, instead of throwing an exception.🛠 Improvements
MoneyBag::getAmount() now accepts an ISO numeric currency code as well✨ New methods
CurrencyConverter::convertToRational() converts to a RationalMoney (#22)Performance improvement when calling Money::formatTo() many times for the same locale.
Added support for brick/math version 0.8.
Breaking Changes
BigRational::toMoney() has been removed, use BigRational::to() instead;BigRational::__toString() now always outputs the amount in non-simplified rational form.New methods
BigRational::simplified() returns a copy of the money with the amount simplified.ISO currency list update.
ISO currency list update.
Money::formatTo() can now format the amount as a whole number:
formatTo(string $locale, bool $allowWholeNumber = false) : string
By default, formatTo() always outputs all the fraction digits:
Money::of('23.5', 'USD')->formatTo('en_US'); // $23.50
Money::of(23, 'USD')->formatTo('en_US'); // $23.00
But can now be allowed to return the whole number by passing true as a second argument:
Money::of('23.5', 'USD')->formatTo('en_US', true); // $23.50
Money::of(23, 'USD')->formatTo('en_US', true); // $23
Note that this version now requires brick/math version 0.7.3. This is not a BC break. If you've locked your composer.json to an earlier version, you will just not be able to install brick/money version 0.3.2.
ISO currency list update.
New methods:
CurrencyConversionException::getSourceCurrencyCode()CurrencyConversionException::getTargetCurrencyCode()This allows to programmatically get the failing currency pair when an exchange rate is not available.
Breaking change:
CurrencyConversionException constructor signature changedAlthough this is technically a breaking change and requires a version bump, your code is unlikely to be affected, unless you're creating CurrencyConversionException instances manually (you shouldn't).
ISO currency list update.
Backports from 0.2.x:
CustomContext::getScale()Money::formatTo() now always respects the scale of the MoneyMoney::allocate() incorrectly allocated negative moniesBug fix: Money::allocate() incorrectly allocated negative monies.
How can I help you explore Laravel packages today?