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.