ulabox / money
使用 BCMath 的另一种 PHP 实现 Money 值对象
Requires
- php: ^7.1 || ^8.0
Requires (Dev)
- phpunit/phpunit: ^6.4
README
PHP 7.1+ Money 库使用 BCMath,受 Mathias Verraes 和 Commerce Guys 的工作启发。使用 1.x 系列,以实现 PHP 5.4+ 兼容性。
动机
使用浮点数表示货币值是错误的,你可能会丢失精度并得到奇怪的结果。最好使用一个使用整数或 BCMath 的 Money 值对象。这个库使用后者。
为什么还需要另一个 Money 库?已经有太多类似的库了!
是的,但其中大部分使用整数作为内部货币表示,这并不符合我们的需求!在大多数情况下使用整数是可以的,而且性能更好,但有时候你需要进行小数位计算。例如,西班牙的汽油定价就是以欧分十分之一或 1.001 € 计算的。增值税计算也可以利用 BCMath 这样的任意精度库提供的额外精度。我们是一家电子商务公司,我们需要这种额外的精度,尤其是在计算折扣时。
为什么使用 BCMath 而不是 GMP?
虽然 GMP 库性能更好,但其 PHP 扩展中的实现缺乏十进制算术,它只能处理整数。有一个 提案 要将十进制实现添加到 PHP 扩展,但这仍然是一个正在进行中的工作。
安装
使用 composer 安装库。只需运行以下命令
$ composer require ulabox/money:@stable
用法
<?php use Money\Money; $tenEUR = Money::EUR('10.00'); $twentyEUR = $tenEUR->add($tenEUR); $fifteenEUR = $twentyEUR->subtract(Money::EUR('5.00')); assert($tenEUR->isLessThan($twentyEUR)); assert($fifteenEUR->isGreaterThan($tenEUR)); assert($fifteenEUR->equals(Money::EUR('15.00')));
动态刻度
从版本 2.x 开始,底层 BCMath 操作的刻度可以改变,以适应您的需求。
默认情况下,Money 类的刻度为 4。您可以在创建 Money 对象时或在进行乘除运算时修改刻度。在四舍五入数字时也会改变刻度。对于加法和减法运算,使用的刻度是两个操作数中最大的。
与 Doctrine 2.5 集成
从版本 2.5 开始,Doctrine 支持使用他们所说的 Embeddables 处理值对象。请注意,这个 Money 对象内部还有一个 Currency VO,这是一个嵌套在嵌套中的可嵌入对象。Doctrine 应该可以很好地处理嵌套的可嵌入对象。
我们建议将 amount
字段映射到 decimal
类型。十进制字段不会丢失精度,并且由 Doctrine 转换为字符串类型,这正是我们在使用 BCMath 时所需要的。货币 code
字段应映射到 string
类型。下面是一个具有 price
Money 字段的 Product
实体的示例模式。
Product:
type: entity
embedded:
price:
class: Money\Money
Money\Money:
type: embeddable
fields:
amount: { type: decimal, precision: 10, scale: 2 }
embedded:
currency:
class: Money\Currency
Money\Currency
type: embeddable
fields:
code: { type: string, length: 3 }
免责声明
我们旨在使这个库尽可能简单。这意味着我们不需要在 Money 类内部有大量的计算操作,如果您计划在 PR 中投入宝贵的时间,请记住这一点!但当然,这可以按我们的意愿改变 :p
由于我们的系统中有一些使用自定义货币代码的虚假货币,我们没有检查货币代码是否与有效 ISO 代码列表相匹配。