Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Money Laravel Package

sebastian/money

Sebastian/money is an abandoned PHP value object library for monetary values using a currency's smallest unit, with formatting and currency subclasses. It was intended as a PHPUnit example, not for production; use moneyphp/money instead.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: sebastian/money is abandoned (last release 1.6.0, 2013-11-25). The author explicitly states it was an example for PHPUnit features, not production code. Treat this assessment as "how you'd use it if it's already in the codebase," and default to moneyphp/money for anything new.

Minimal setup:

composer require sebastian/money:^1.6

Pin the exact minor (1.6.*) — there will be no patches, so a loose constraint buys you nothing but surprise if a mirror or fork republishes a tag.

Where to look first (the package is tiny — read it all in one sitting):

  1. src/Money.php — constructor, getAmount(), getConvertedAmount(), arithmetic methods, equals().
  2. src/Currency.php — a thin value object around an ISO code string.
  3. src/Exception/ — the exception types you'll be catching.

First use case — represent and total a line item:

use SebastianBergmann\Money\Currency;
use SebastianBergmann\Money\Money;

$currency = new Currency('EUR');

// 100 = smallest unit, i.e. 1.00 EUR
$line = new Money(100, $currency);
$total = $line->add(new Money(250, $currency)); // 3.50 EUR

print $total->getAmount();          // 350   <- what you persist
print $total->getConvertedAmount(); // 3.50  <- what you display

Rule of thumb from day one: getAmount() is the truth, getConvertedAmount() is presentation. Store the integer, format at the edge.

Implementation Patterns

Persist amount + currency code, never a float. Schema: amount BIGINT NOT NULL, currency CHAR(3) NOT NULL. Hydrate in a repository or Eloquent cast:

protected $casts = ['amount' => 'integer'];

public function money(): Money
{
    return new Money($this->amount, new Currency($this->currency));
}

Wrap construction in one factory. User input arrives as "1.234,56", "$12.00", 1.5, etc. Centralize parsing so the rest of the app only ever sees Money:

final class MoneyFactory
{
    public static function fromDecimalString(string $value, string $code): Money
    {
        // avoid float math entirely: shift the decimal, then cast
        $normalized = preg_replace('/[^\d.\-]/', '', $value);
        $amount = (int) round(((float) $normalized) * 100); // see Gotchas

        return new Money($amount, new Currency($code));
    }
}

Keep arithmetic inside the domain layer. add()/subtract() return new instances; assign them. Services should accept and return Money, and only convert at the boundary (controller, API resource, Blade view).

Format with intl, not with string concatenation:

$fmt = new \NumberFormatter('de_DE', \NumberFormatter::CURRENCY);
echo $fmt->formatCurrency($money->getConvertedAmount(), $money->getCurrency()->getCode());

Currency guard at the edges. Do comparisons before arithmetic ($a->getCurrency()->getCode() === $b->getCurrency()->getCode()), and throw your own domain exception rather than relying on whatever the library throws. This keeps the library's exception surface out of your domain.

Splitting/allocating amounts (invoices, tax lines, discounts): don't hope for a built-in allocate(); verify against src/Money.php in your version. Safer to write your own integer split — divide getAmount() with intdiv(), distribute the remainder one unit at a time — and construct Money objects from the results. That logic is ten lines and stays testable.

Integration tip: wrap the library behind your own Money interface if the codebase is non-trivial. You are almost certainly going to migrate to moneyphp/money, and a one-file adapter (getAmount() → getMinorAmount(), Currency → Money::EUR()) turns a codebase-wide rewrite into a single class change.

Gotchas and Tips

  • The decimal exponent is hardcoded to 2. getConvertedAmount() divides by 100. That is wrong for JPY (0), BHD/KWD/JOD (3), and ISK. If you accept non-2-exponent currencies, compute base units yourself from an exponent table and never call getConvertedAmount().
  • Currency is not validated against ISO 4217. new Currency('XXX') will happily construct. Validate codes at your boundary (config whitelist or intl's currency list).
  • Immutability gotcha: $money->add($other); silently does nothing. Static analysis won't catch it. Consider enabling a "no unused expression" style rule (PHPStan/Psalm) or make the habit of always assigning.
  • Float contamination: (float) $money->getConvertedAmount() * 3 reintroduces exactly the rounding bugs the value object exists to prevent. Multiply the integer: new Money($m->getAmount() * 3, $m->getCurrency()).
  • Rounding on fromString/factory: round($x * 100) uses PHP's default half-up-away-from-zero; bankers rounding differs. Document the rule and test with 0.005, 1.005, and negative values.
  • PHP 8 compatibility: last touched in 2013. The code is small and mostly fine, but expect deprecation notices (implicit nullable params, dynamic properties in subclasses) and no type declarations, so your IDE and PHPStan will be flying blind. Add stubs or a baseline rather than patching a vendored package.
  • Exception surface: arithmetic on mismatched currencies throws from the library. Catch narrowly and rethrow as your domain exception — don't let SebastianBergmann\Money\Exception\* leak into controllers.
  • Debugging: var_dump() of the object shows the integer amount only — no formatted value, no __toString(). Add a helper Money::__debugInfo-equivalent or a test helper that prints amount . ' ' . code so failing assertions are readable.
  • Migration exit ramp: map getAmount() → getMinorAmount()/getAmount(), new Currency('EUR') → Money::EUR($amount), and getConvertedAmount() → formatToDecimal()/getAmount()->toFloat() in moneyphp/money. Run both implementations against a fixture table of amounts (including JPY and BHD) before cutting over — that fixture is the cheapest insurance you can buy here.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment