Commit 4a91238b authored by Thorsten Buss's avatar Thorsten Buss
Browse files

* implement/correct/optmize/locale parseMoneyString method

* add tests for MoneyStringParsing
* add "moneyStringParsing" to construct and setAmount
* fix Money::getInstance
* fix static legibly and format method
parent a9d67898
......@@ -60,11 +60,13 @@ class Currency
/**
* return instance of MOneyObj - for single use to reduce memory usage in loops
* @param null $currency
* @return Money
* @return Currency
*/
public static function getInstance($currency = null) {
if ($currency instanceof Currency)
return $currency;
// get isocode from currency, direct or default
$iso_code = $currency instanceof Currency ? $currency->getIsostring() : ($currency ? : Currency::getDefaultCurrency());
$iso_code = $currency ?: Currency::getDefaultCurrency();
if (!array_key_exists($iso_code, static::$instances)) {
static::$instances[$iso_code] = new static($iso_code);
}
......@@ -267,7 +269,7 @@ class Currency
* @return string
*/
public static function format($amount, $currency=null, $params = array()) {
return Money::getInstance($currency, $amount)->format($params);
return Money::getInstance($currency)->setAmount($amount)->format($params);
}
/**
......@@ -281,7 +283,7 @@ class Currency
* @return string
*/
public static function legibly($amount, $currency=null, $params = array()) {
return Money::getInstance($currency, $amount)->getAmount(true, $params);
return Money::getInstance($currency)->setAmount($amount)->getAmount(true, $params);
}
}
......
......@@ -32,32 +32,42 @@ class Money
* Create a Money instance
* @param integer $amount Amount, expressed in the smallest units of $currency (eg cents)
* @param \Money\Currency|string $currency as Obj or isoString
* @param bool $parseAmountAsMoneyString amount ist unit (not subuit) and in moneyformat (maybe , as dec_mark)
* @throws \Money\InvalidArgumentException
*/
public function __construct($amount, Currency $currency=null)
*/
public function __construct($amount, Currency $currency=null, $parseAmountAsMoneyString=false)
{
if (!is_numeric($amount)) {
if (!is_int($amount) && !ctype_digit($amount)) { // only numbers(int) - as string or int type
throw new InvalidArgumentException("The first parameter of Money must be an integer. It's the amount, expressed in the smallest units of currency (eg cents)");
}
$this->amount = $amount;
if (!$currency instanceof Currency)
$currency = new Currency($currency ? : Currency::getDefaultCurrency());
$this->currency = $currency;
$this->currency = Currency::getInstance($currency);
$this->setAmount($amount, $parseAmountAsMoneyString);
}
/**
* return instance of MOneyObj - for single use to reduce memory usage in loops
* return new instance of MoneyObj
* @param null $currency
* @param int $amount
* @param bool $parseAmountAsMoneyString amount ist unit (not subuit) and in moneyformat (maybe , as dec_mark)
* @return Money
*/
public static function newInstance($currency=null, $amount=0, $parseAmountAsMoneyString = false) {
return new static($amount, $currency, $parseAmountAsMoneyString);
}
/**
* return saved instance of a MoneyObj with given currency - for single use to reduce memory usage in loops
* WARNING: NOT SAFE FOR EXTERNAL USAGE - ONLY INTERN OBJ CACHE
* @param null $currency
* @return Money
*/
public static function getInstance($currency=null, $amount=0) {
public static function getInstance($currency=null) {
// get isocode from currency, direct or default
$iso_code = $currency instanceof Currency ? $currency->getIsostring() : ($currency ? : Currency::getDefaultCurrency());
if (!array_key_exists($iso_code, static::$instances)) {
static::$instances[$iso_code] = new static($amount, new Currency($iso_code));
}
return static::$instances[$iso_code];
static::$instances[$iso_code] = new static(0, $currency);
}
return static::$instances[$iso_code];
}
/**
......@@ -75,9 +85,11 @@ class Money
/**
* change the amount
* @param int $amount amount in subunit
* @param bool $parseAmountAsMoneyString amount ist unit (not subuit) and in moneyformat (maybe , as dec_mark)
* @return $this
*/
public function setAmount($amount) {
$this->amount = $amount;
public function setAmount($amount, $parseAmountAsMoneyString = false) {
$this->amount = $parseAmountAsMoneyString ? self::parseMoneyString($amount, $this->getCurrency()) : $amount;
return $this;
}
......@@ -296,30 +308,29 @@ class Money
return $this->amount < 0;
}
/**
* @param $string
* @throws \Money\InvalidArgumentException
* @return int
*/
public static function stringToUnits( $string )
{
//@todo extend the regular expression with grouping characters and eventually currencies
if (!preg_match("/(-)?(\d+)([.,])?(\d)?(\d)?/", $string, $matches)) {
throw new InvalidArgumentException("The value could not be parsed as money");
}
$units = $matches[1] == "-" ? "-" : "";
$units .= $matches[2];
$units .= isset($matches[4]) ? $matches[4] : "0";
$units .= isset($matches[5]) ? $matches[5] : "0";
return (int) $units;
}
/**
* @see Money::parseMoneyString()
*/
public static function stringToUnits($string, $currency=null) {
return self::parseMoneyString($string, $currency);
}
/**
* @see Money::stringToUnits()
* parse moneyformated string and returns amount in subunit
* @param $string
* @param null $currency
* @return int subunit from Money-string
* @throws InvalidArgumentException
*/
public static function parseMoneyString($string) {
return self::stringToUnits($string);
public static function parseMoneyString($string, $currency = null) {
$currency = Currency::getInstance($currency);
$t = str_replace('.', '\.', $currency->getThousandsSeparator());
$d = str_replace('.', '\.', $currency->getDecimalMark());
if (!preg_match("/^([-+])?(?:0|([1-9]\d{0,2})(?:$t?(\d{3}))*)(?:$d(\d+))?$/", $string, $matches)) {
throw new InvalidArgumentException(sprintf('The string "%s" could not be parsed as money', $string));
}
$units = (float)(@$matches[1] . @$matches[2] . @$matches[3] . '.' . @$matches[4]) * 100;
return round($units);
}
/**
......
......@@ -46,14 +46,6 @@ class MoneyTest extends MoneyTestCase {
$money = new Money(0.01, new Currency('EUR'));
}
/**
* @expectedException Money\InvalidArgumentException
*/
public function testStringThrowsException()
{
$money = new Money('100', new Currency('EUR'));
}
public function testEquality()
{
$m1 = new Money(100, new Currency('EUR'));
......@@ -211,14 +203,20 @@ class MoneyTest extends MoneyTestCase {
$this->assertFalse(Money::EUR(-1)->isPositive());
}
public static function provideStrings()
public static function provideStringsUSD()
{
return array(
array("1000", 100000),
array("1000.0", 100000),
array("1000.00", 100000),
array("1000.1", 100010),
array("1000.11", 100011),
array("1,000.11", 100011),
array("0.01", 1),
array("1", 100),
array("0.001", 0),
array("0.005", 1),
array("0.009", 1),
array("1", 100),
array("-1000", -100000),
array("-1000.0", -100000),
array("-1000.00", -100000),
......@@ -233,11 +231,46 @@ class MoneyTest extends MoneyTestCase {
}
/**
* @dataProvider provideStrings
* @dataProvider provideStringsUSD
*/
public function testStringToUnitsUSD($string, $units)
{
$this->assertEquals($units, Money::stringToUnits($string, 'USD'));
}
public static function provideStringsEUR()
{
return array(
array("1000", 100000),
array("1000,0", 100000),
array("1000,00", 100000),
array("1000,1", 100010),
array("1000,11", 100011),
array("1.000,11", 100011),
array("0,01", 1),
array("0,001", 0),
array("0,005", 1),
array("0,009", 1),
array("1", 100),
array("-1000", -100000),
array("-1000,0", -100000),
array("-1000,00", -100000),
array("-0,01", -1),
array("-1", -100),
array("+1000", 100000),
array("+1000,0", 100000),
array("+1000,00", 100000),
array("+0,01", 1),
array("+1", 100)
);
}
/**
* @dataProvider provideStringsEUR
*/
public function testStringToUnits($string, $units)
public function testStringToUnitsEUR($string, $units)
{
$this->assertEquals($units, Money::stringToUnits($string));
$this->assertEquals($units, Money::stringToUnits($string, 'EUR'));
}
public function testGetAmount() {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment