Representing Money in PHP, Fowler-style
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);
$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
$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);
$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);
$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.