Money PHP Library

repository·master·Indexed 26 days ago

https://github.com/moneyphp/money

A PHP library for handling monetary values safely using immutable value objects. It avoids floating-point precision issues by using integers and strings internally and requires the BCMath extension. The library provides tools for arithmetic operations, ratio-based allocation, currency conversion via FixedExchange or Swap, and support for ISO 4217 and Bitcoin (XBT) currencies.

Tokens
10.7K
Snippets
39
Records
49
Agent score
89%

What's inside moneyphp/money

  1. Integrate Swap for currency conversion

    master

    You can use the Swap library to provide real-time exchange rates. First, install it via Composer:

    $ composer require florianv/swap

    Then, use Money\Exchange\SwapExchange to wrap the Swap\SwapInterface implementation.

    use Money\Money;
    use Money\Converter;
    use Money\Currencies\ISOCurrencies;
    use Money\Exchange\SwapExchange;
    
    // $swap = Implementation of \Swap\SwapInterface
    $exchange = new SwapExchange($swap);
    
    $converter = new Converter(new ISOCurrencies(), $exchange);
    $eur100 = Money::EUR(100);
    $usd125 = $converter->convert($eur100, new Currency('USD'));
    [$usd125, $pair] = $converter->convertAndReturnWithCurrencyPair($eur100, new Currency('USD'));
  2. Work with immutable Money objects

    master
    All Money objects are immutable. Performing arithmetic operations (like subtract()) does not modify the existing object; instead, these methods return a new Money instance representing the result. You must capture the return value to reflect changes in your application logic.
  3. Integrate Exchanger for currency conversion

    master

    You can use the Exchanger framework to provide exchange rates. First, install it via Composer:

    $ composer require florianv/exchanger

    Then, use Money\Exchanger\ExchangerExchange to wrap an implementation of \Exchanger\Contract\ExchangeRateProvider.

    use Money\Money;
    use Money\Converter;
    use Money\Currencies\ISOCurrencies;
    use Money\Exchanger\ExchangerExchange;
    
    // $exchanger = Implementation of \Exchanger\Contract\ExchangeRateProvider
    $exchange = new ExchangerExchange($exchanger);
    
    $converter = new Converter(new ISOCurrencies(), $exchange);
    $eur100 = Money::EUR(100);
    $usd125 = $converter->convert($eur100, new Currency('USD'));
    [$usd125, $pair] = $converter->convertAndReturnWithCurrencyPair($eur100, new Currency('USD'));
  4. Allocate money to N equal targets

    master

    Use the allocateTo(int $n) method to divide a Money object into $n approximately equal parts.

    Similar to ratio allocation, the method ensures the total sum is preserved by distributing any remainder cents across the targets. The resulting array contains $n Money objects.

    $value = Money::EUR(800);           // €8.00
    
    $result = $value->allocateTo(3);    // $result = [€2.67, €2.67, €2.66]
  5. Convert Money using a Fixed Exchange

    master

    To convert a Money instance using predefined rates, use the Money\Converter class in conjunction with Money\Exchange\FixedExchange. The FixedExchange accepts an associative array where keys are base currencies and values are arrays of target currencies and their corresponding conversion ratios.

    use Money\Converter;
    use Money\Currency;
    use Money\Currencies\ISOCurrencies;
    use Money\Exchange\FixedExchange;
    
    $exchange = new FixedExchange([
        'EUR' => [
            'USD' => '1.25'
        ]
    ]);
    
    $converter = new Converter(new ISOCurrencies(), $exchange);
    
    $eur100 = Money::EUR(100);
    $usd125 = $converter->convert($eur100, new Currency('USD'));
  6. Instantiate a Money object

    master

    To create a Money object, you must provide the amount in the smallest unit of the currency (e.g., cents for USD) as an integer or a compatible string. You can use the standard constructor or the currency-specific static helper methods.

    Note that the Money object only supports integer-like values. Passing unsupported formats like strings with leading zeros will throw a \InvalidArgumentException.

    use Money\Currency;
    use Money\Money;
    
    // Using the constructor
    $fiver = new Money(500, new Currency('USD'));
    
    // Using the shorthand static method
    $fiver = Money::USD(500);
  7. Enable reverse currency conversion with ReversedCurrenciesExchange

    master

    If you want an exchange to automatically resolve the reverse of a defined CurrencyPair (e.g., if EUR/USD is defined, allow USD/EUR by calculating the ratio as 1 divided by the original ratio), wrap your exchange in Money\Exchange\ReversedCurrenciesExchange. This is useful for avoiding the need to define every pair in both directions.

    use Money\Converter;
    use Money\Currency;
    use Money\Currencies\ISOCurrencies;
    use Money\Exchange\FixedExchange;
    use Money\Exchange\ReversedCurrenciesExchange;
    
    $exchange = new ReversedCurrenciesExchange(new FixedExchange([
        'EUR' => [
            'USD' => '1.25'
        ]
    ]));
    
    $converter = new Converter(new ISOCurrencies(), $exchange);
    
    $usd125 = Money::USD(125);
    $eur100 = $converter->convert($usd125, new Currency('EUR'));
  8. Use Teller to replace float math for monetary calculations

    master

    The Teller class is designed to ease the transition from float-based math to Money objects in legacy codebases. It allows you to perform monetary calculations using float or string values while avoiding the precision errors associated with standard float arithmetic.

    Note: A Teller instance is bound to a single currency; you cannot use multiple currencies with a single Teller instance.

    // before (unsafe float math)
    $price = 234.56;
    $discount = 0.05;
    $discountAmount = $price * $discount; // 11.728
    
    // after (using Teller)
    $teller = \Money\Teller::USD();
    $discountAmount = $teller->multiply($price, $discount); // '11.73'
  9. Allocate money by ratios

    master

    Use the allocate() method to divide a Money object among multiple targets based on provided integer ratios.

    To prevent the creation of fractional cents, the library follows these rules:

    1. It calculates each target's share by rounding down.
    2. It identifies the remainder caused by rounding.
    3. It distributes the remainder one by one to the targets that lost the most value due to rounding, until the total amount is fully allocated.

    Note: The order of the ratios in the array determines the order in which targets receive the remainder cents. The target corresponding to the first ratio in the array has priority for receiving remainder cents.

    use Money\Money;
    
    $profit = Money::EUR(5);
    // The order of ratios determines priority for remainder allocation
    list($my_cut, $investors_cut) = $profit->allocate([70, 30]);
    // $my_cut is 4 cents, $investors_cut is 1 cent
    
    // Reversing the order changes who gets the remainder
    list($investors_cut, $my_cut) = $profit->allocate([30, 70]);
    // $my_cut is 3 cents, $investors_cut is 2 cents