Representing Money in PHP, Fowler-style
By
Mathias Verraes
Published on 04 April 2011
By
Mathias Verraes
Published on 04 April 2011
Whenever working with values in object oriented programming, it’s often a good idea to wrap them in a ValueObject. Money is a perfect candidate for a ValueObject: When talking about money, numbers are meaningless if they are not combined with a currency.
I’ve been using a very simple version of the Money pattern as described in Martin Fowler’s PoEAA. I couldn’t find a PHP implementation anywhere, so I decided to make my own little open source library for it. You can find it on my GitHub account (where else?).
An important aspect of ValueObjects is their immutability:
Let’s say Jim and Hannah both want to buy a copy of book priced at EUR 25.
<?php
$jim_price = $hannah_price = new Money(2500, new Euro);
Jim has a coupon for EUR 5.
<?php
$coupon = new Money(500, new Euro);
$jim_price->subtract($coupon);
Because $jim_price and $hannah_price are the same object, you’d expect Hannah to now have the reduced price as well. To prevent this problem, Money objects are immutable. With the code above, both $jim_price and $hannah_price are still EUR 25:
<?php
$jim_price->equals($hannah_price); // true
The correct way of doing operations is:
<?php
$jim_price = $jim_price->subtract($coupon);
$jim_price->lessThan($hannah_price); // true
$jim_price->equals(Money::euro(2000)); // true
My company made a whopping profit of 5 cents, which has to be divided amongst myself (70%) and my investor (30%). Cents can’t be divided, so I can’t give 3.5 and 1.5 cents. If I round up, I get 4 cents, the investor gets 2, which means I need to conjure up an additional cent. Rounding down to 3 and 1 cent leaves me 1 cent. Apart from re-investing that cent in the company, the best solution is to keep handing out the remainder until all money is spent. In other words:
<?php
$profit = new Money(5, new Euro);
list($my_cut, $investors_cut) = $profit->allocate(70, 30);
Now $my_cut is 4 cents, and $investors_cut is 1 cent. The order in which you allocate the the money is important:
<?php
list($investors_cut, $my_cut) = $profit->allocate(30, 70);
Now $my_cut is 3 cents, and $investors_cut is 2 cents.
At the moment my Money class has all the features that Fowler lists. You can see some more example by looking at the unit tests. I hope to add a lot stuff more if time permits: currency conversion, parsing of strings like ‘$2.00’ and ‘USD 2.00’, dealing with major units and subunits in currencies, etc. In any case, I hope it’s useful to somebody in it’s present form.
Follow me on
Mastodon,
Bluesky,
LinkedIn, or use the
Atom feed for updates.
Support my writing by buying my book Design and Reality.