Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MoneyRange implementation #514

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php_cs.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ $finder = PhpCsFixer\Finder::create()
->exclude('resources')
->in(__DIR__)
->notPath('src/MoneyFactory.php')
->notPath('src/MoneyRangeFactory.php')
;

return PhpCsFixer\Config::create()
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
],
"update-currencies": [
"cp vendor/moneyphp/iso-currencies/resources/current.php resources/currency.php",
"php resources/generate-money-factory.php"
"php resources/generate-money-factory.php",
"php resources/generate-money-range-factory.php"
]
},
"extra": {
Expand Down
4 changes: 2 additions & 2 deletions resources/currency.php
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@
'PHP' =>
array (
'alphabeticCode' => 'PHP',
'currency' => 'Philippine Piso',
'currency' => 'Philippine Peso',
'minorUnit' => 2,
'numericCode' => 608,
),
Expand Down Expand Up @@ -1122,7 +1122,7 @@
'UYI' =>
array (
'alphabeticCode' => 'UYI',
'currency' => 'Uruguay Peso en Unidades Indexadas (URUIURUI)',
'currency' => 'Uruguay Peso en Unidades Indexadas (UI)',
'minorUnit' => 0,
'numericCode' => 940,
),
Expand Down
64 changes: 64 additions & 0 deletions resources/generate-money-range-factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

require __DIR__.'/../vendor/autoload.php';

use Money\Currencies;

$buffer = <<<PHP
<?php

namespace Money;

/**
* This is a generated file. Do not edit it manually!
*
PHPDOC
*/
trait MoneyRangeFactory
{
/**
* Convenience factory method for a MoneyRange object.
*
* <code>
* \$fiveToTenDollars = MoneyRange::USD(500, 1000);
* </code>
*
* @param string \$method
* @param array \$arguments
*
* @return MoneyRange
*
* @throws \InvalidArgumentException If either amount is not integer(ish)
*/
public static function __callStatic(\$method, \$arguments)
{
return new MoneyRange(
new Money(\$arguments[0], new Currency(\$method)),
new Money(\$arguments[1], new Currency(\$method))
);
}
}

PHP;

$methodBuffer = '';

$currencies = new Currencies\AggregateCurrencies([
new Currencies\ISOCurrencies(),
new Currencies\BitcoinCurrencies(),
]);

$currencies = iterator_to_array($currencies);

usort($currencies, function (\Money\Currency $a, \Money\Currency $b) {
return strcmp($a->getCode(), $b->getCode());
});

/** @var \Money\Currency[] $currencies */
foreach ($currencies as $currency) {
$methodBuffer .= sprintf(" * @method static MoneyRange %s(string \$amount)\n", $currency->getCode());
}

$buffer = str_replace('PHPDOC', rtrim($methodBuffer), $buffer);

file_put_contents(__DIR__.'/../src/MoneyRangeFactory.php', $buffer);
283 changes: 283 additions & 0 deletions src/MoneyRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
<?php

namespace Money;

/**
* Money Range Value Object.
*
* @author Jaik Dean <[email protected]>
*/
final class MoneyRange implements \JsonSerializable
{
use MoneyRangeFactory;

/**
* @var Money
*/
private $start;

/**
* @var Money
*/
private $end;

/**
* @param Money $start Start value
* @param Money $end End value
*
* @throws \InvalidArgumentException If the start and end currencies don't match, or the start value is greater than the end value
*/
public function __construct(Money $start, Money $end)
{
if (!$start->isSameCurrency($end)) {
throw new \InvalidArgumentException('Currencies must be identical');
}

if ($start->greaterThan($end)) {
throw new \InvalidArgumentException('End value must be equal to or larger than start value');
}

$this->start = $start;
$this->end = $end;
}

/**
* Get the start value of this range.
*
* @return Money
*/
public function getStart()
{
return $this->start;
}

/**
* Get the start value of this range.
*
* @return Money
*/
public function getEnd()
{
return $this->end;
}

/**
* Checks whether the value represented by this object equals to the other.
*
* @param MoneyRange $other
*
* @return bool
*/
public function equals(MoneyRange $other)
{
return $this->isSameCurrency($other)
&& $this->start->equals($other->start)
&& $this->end->equals($other->end)
;
}

/**
* Get the mid point value of this range.
*
* @param int $roundingMode
*
* @return Money
*/
public function midPoint($roundingMode = Money::ROUND_HALF_UP)
{
return $this->start->add(
$this->end->subtract($this->start)->divide(2, $roundingMode)
);
}

/**
* Returns a new MoneyRange instance based on the current one using the new start value.
*
* @param Money $start New start value
*
* @return MoneyRange
*/
public function setStart(Money $start)
{
return new MoneyRange($start, $this->end);
}

/**
* Returns a new MoneyRange instance based on the current one using the new end value.
*
* @param Money $end New end value
*
* @return MoneyRange
*/
public function setEnd(Money $end)
{
return new MoneyRange($this->start, $end);
}

/**
* Checks whether a Money or MoneyRange has the same Currency as this.
*
* @param Money|MoneyRange $other
*
* @return bool
*/
public function isSameCurrency($other)
{
if (!$other instanceof Money && !$other instanceof MoneyRange) {
throw new \InvalidArgumentException(
\sprintf(
'Argument passed must be %s or %s.',
Money::class,
MoneyRange::class
)
);
}

return $this->start->getCurrency()->equals($other->getCurrency());
}

/**
* Asserts that a Money or MoneyRange has the same currency as this.
*
* @param Money|MoneyRange $other
*
* @throws \InvalidArgumentException If $other has a different currency
*/
private function assertSameCurrency($other)
{
if (!$other instanceof Money && !$other instanceof MoneyRange) {
throw new \InvalidArgumentException(
\sprintf(
'Argument passed must be %s or %s.',
Money::class,
MoneyRange::class
)
);
}

if (!$this->isSameCurrency($other)) {
throw new \InvalidArgumentException('Currencies must be identical');
}
}

/**
* Checks whether the range represented by this object contains a value.
*
* @param Money $value
*
* @return bool
*/
public function contains(Money $value)
{
return $this->isSameCurrency($value)
&& $this->start->lessThanOrEqual($value)
&& $this->end->greaterThanOrEqual($value);
}

/**
* Checks whether the range represented by this object is greater than the value.
*
* @param Money $value
*
* @return bool
*/
public function greaterThan(Money $value)
{
return $this->start->greaterThan($value);
}

/**
* Checks whether the range represented by this object is less than the value.
*
* @param Money $value
*
* @return bool
*/
public function lessThan(Money $value)
{
return $this->end->lessThan($value);
}

/**
* Returns the currency of this object.
*
* @return Currency
*/
public function getCurrency()
{
return $this->start->getCurrency();
}

/**
* Returns a new MoneyRange object that represents
* the multiplied value by the given factor.
*
* @param float|int|string $multiplier
* @param int $roundingMode
*
* @return MoneyRange
*/
public function multiply($multiplier, $roundingMode = Money::ROUND_HALF_UP)
{
$start = $this->start->multiply($multiplier, $roundingMode);
$end = $this->end->multiply($multiplier, $roundingMode);

return new MoneyRange($start, $end);
}

/**
* Returns a new MoneyRange object that represents
* the divided value by the given factor.
*
* @param float|int|string $divisor
* @param int $roundingMode
*
* @return MoneyRange
*/
public function divide($divisor, $roundingMode = Money::ROUND_HALF_UP)
{
$start = $this->start->divide($divisor, $roundingMode);
$end = $this->end->divide($divisor, $roundingMode);

return new MoneyRange($start, $end);
}

/**
* @return MoneyRange
*/
public function absolute()
{
$start = $this->start->absolute();
$end = $this->end->absolute();

if ($start->greaterThan($end)) {
return new MoneyRange($end, $start);
}

return new MoneyRange($start, $end);
}

/**
* Checks if the value represented by this object contains zero.
*
* @return bool
*/
public function containsZero()
{
return $this->contains(new Money(0, $this->start->getCurrency()));
}

/**
* {@inheritdoc}
*
* @return array
*/
public function jsonSerialize()
{
return [
'start' => $this->start->getAmount(),
'end' => $this->end->getAmount(),
'currency' => $this->start->getCurrency(),
];
}
}
Loading