byrokrat / amount
Requires
- php: >=7.0
- ext-bcmath: *
Requires (Dev)
- ext-intl: *
- alcohol/iso4217: ^3
- phpunit/phpunit: ^6
README
已废弃!此包已停止维护,将不再更新。请查看moneyphp/money。
Amount
货币金额的值对象。
功能
- 不可变值对象。
- 使用bcmath扩展进行任意浮点精度算术。
- 货币支持以防止货币混合。
- 开箱即用的ISO 4217货币支持。
- 简单接口用于定义新货币。
- 支持多种舍入策略。
- 支持根据比例分配金额。
- 支持瑞典直接借记系统中使用的信号字符串格式。
安装
composer require byrokrat/amount
使用方法
use byrokrat\amount\Amount; $amount = new Amount('100.6'); // outputs 1 (true) echo $amount->isGreaterThan(new Amount('50')); // round to 0 decimal digits $roundedAmount = $amount->roundTo(0); // outputs 101.00 echo $roundedAmount;
API
Amount
定义了以下API
方法签名 | 返回值 | 描述 |
---|---|---|
__construct(string $amount) | Amount | 创建新实例 |
getAmount() | string | 获取原始金额 |
roundTo([int $precision, [Rounder $rounder]]) | Amount | 获取舍入到$precision的Amount |
getString([int $precision, [Rounder $rounder]]) | string | 获取金额字符串 |
__tostring() | string | 获取金额字符串 |
getInt([Rounder $rounder]) | integer | 获取整数金额(警告) |
getFloat([int $precision, [Rounder $rounder]]) | float | 获取浮点金额(警告) |
getSignalString([Rounder $rounder]) | string | 获取信号字符串 |
add(Amount $amount) | Amount | 获取增加$amount后的新Amount |
subtract(Amount $amount) | Amount | 获取减去$amount后的新Amount |
multiplyWith(mixed $amount) | Amount | 获取乘以$amount后的新Amount |
divideBy(mixed $amount) | Amount | 获取除以$amount后的新Amount |
compareTo(Amount $amount) | integer | 等于返回0,大于返回1,否则返回-1 |
equals(Amount $amount) | boolean | 检查是否等于金额 |
isLessThan(Amount $amount) | boolean | 检查是否小于金额 |
isLessThanOrEquals(Amount $amount) | boolean | 检查是否小于或等于金额 |
isGreaterThan(Amount $amount) | boolean | 检查是否大于金额 |
isGreaterThanOrEquals(Amount $amount) | boolean | 检查是否大于或等于金额 |
isZero() | boolean | 检查金额是否为零 |
isPositive() | boolean | 检查金额是否大于零 |
isNegative() | boolean | 检查金额是否小于零 |
getInverted() | Amount | 获取符号反转后的新金额 |
getAbsolute() | Amount | 获取移除负号的新金额 |
allocate(array $ratios, [int $precision]) | Amount[] | 根据比率列表分配金额 |
从其他格式创建金额
浮点数
Amount
包含两个用于处理浮点数的便捷方法。 createFromNumber
可以从浮点数创建一个 Amount 对象,getFloat
可以将 Amount 对象转换为浮点数。应谨慎使用这些方法。
需要注意的是,计算机内部使用二进制浮点数格式,无法完全准确地表示像 0.1、0.2 或 0.3 这样的数字。使用浮点数会导致精度损失。例如,floor((0.1+0.7)*10)
通常会返回 7 而不是预期的 8,因为内部表示可能是类似于 7.9999999999999991118....
因此,浮点数绝不应用于存储货币数据。这些方法存在于不可避免地需要转换为或从原生格式的情况。除非你了解自己在做什么,否则它们不应被使用。
有关更多信息,请参阅 php 手册 或 程序员应了解的关于浮点运算的每件事。
格式化数字
您可以使用静态方法 createFromFormat 从格式化为非标准或依赖于区域的十进制点和分组字符的字符串创建 Amount。
use byrokrat\amount\Amount; $formattedAmount = "2 000:50"; $amount = Amount::createFromFormat($formattedAmount, ":", " "); echo $amount; // outputs 2000.50
信号字符串
信号字符串格式不包含小数点,并且负金额使用字母而不是最后一位数字来表示。使用静态方法 createFromSignalString 从信号字符串创建 Amount。
处理货币
货币子系统有助于防止将不同货币的值混合(例如相加)的错误。货币对象是 Amount
的子类,并以相同的方式工作,增加了它们知道自己的货币定义的功能。
use byrokrat\amount\Currency\SEK; use byrokrat\amount\Currency\EUR; $sek = new SEK('100'); // throws an exception $sek->add(new EUR('1'));
use byrokrat\amount\Currency\SEK; $sek = new SEK('100'); // works as intended, outputs 101.00 echo $sek->add(new SEK('1'));
ISO 4217
在 byrokrat\amount\Currency
命名空间中捆绑了一套全面的 ISO 4217 货币。
请注意,土耳其里拉(ISO 代码 TRY
)表示为 _TRY
,因为 try 是一个保留关键字。
创建新货币
然而,创建新货币很简单。只需从 Currency
类中派生一个子类,并定义 getCurrencyCode()
。
另外,您可能需要重写 getDisplayPrecision()
、getInternalPrecision()
和 getDefaultRounder()
以进一步定义您的货币行为。
兑换
使用 createFromExchange
支持货币兑换。请注意,您必须提供正确的汇率。
use byrokrat\amount\Currency\SEK; use byrokrat\amount\Currency\EUR; // One euro is exchanged into swedish kronas using the exchange rate 9.27198929 // resulting in the value of SEK 9.27198929 echo $sek = SEK::createFromExchange(new EUR('1'), '9.27198929');
货币格式化
可以使用 PHP 内置的 NumberFormatter
对象轻松地格式化货币。
use byrokrat\amount\Currency\EUR; // Create some amount of euros $money = new EUR('1234567.89'); // Create a currency formatter with swedish formatting rules $formatter = new NumberFormatter('sv_SE', NumberFormatter::CURRENCY); // Format euros according to swedish standards, outputs 1 234 567:89 € echo $formatter->formatCurrency($money->getFloat(), $money->getCurrencyCode());
舍入
支持多种舍入策略。要实现自己的舍入策略,请查看 Rounder 接口。
namespace byrokrat\amount; $amount = new Amount('1.5'); // outputs 2 echo $amount->roundTo(0, new Rounder\RoundUp); // outputs 1 echo $amount->roundTo(0, new Rounder\RoundDown); // outputs 1 echo $amount->roundTo(0, new Rounder\RoundTowardsZero); // outputs 2 echo $amount->roundTo(0, new Rounder\RoundAwayFromZero); // outputs 2 echo $amount->roundTo(0, new Rounder\RoundHalfUp); // outputs 1 echo $amount->roundTo(0, new Rounder\RoundHalfDown); // outputs 1 echo $amount->roundTo(0, new Rounder\RoundHalfTowardsZero); // outputs 2 echo $amount->roundTo(0, new Rounder\RoundHalfAwayFromZero); // outputs 2 echo $amount->roundTo(0, new Rounder\RoundHalfToEven); // outputs 1 echo $amount->roundTo(0, new Rounder\RoundHalfToOdd);
有关舍入策略的更多信息,请参阅 维基百科。
分配
分配是将金额按比例划分的过程,这样最小的单位不会被划分(每种货币都有一个不可划分的最小单位),而是传递给下一个接收者。
这些比例可以看作(但不一定必须是)百分比。一百个单位可以分成两个接收者,如下所示
use byrokrat\amount\Amount; $money = new Amount('100'); list($receiverA, $receiverB) = $money->allocate([30, 70]); // outputs 30 echo $receiverA; // outputs 70 echo $receiverB;
当我们分配一个无法均匀划分的值时,分配的优势变得明显。在这种情况下,接收者的顺序很重要。
use byrokrat\amount\Amount; $money = new Amount('0.05'); list($receiverA, $receiverB) = $money->allocate([30, 70]); // outputs 0.03 echo $receiverA; // outputs 0.02 echo $receiverB; list($receiverA, $receiverB) = $money->allocate([70, 30]); // outputs 0.04 echo $receiverA; // outputs 0.01 echo $receiverB;
在这些例子中,使用的不可划分的单位是 0.01
。这是默认行为。可以通过指定 $precision
参数或在您的货币类中覆盖 getDisplayPrecision()
来更改它。