elegant-bro / money
0.3.6
2022-08-24 11:11 UTC
Requires
- php: >=7.3
- ext-bcmath: *
- elegant-bro/interfaces: ^1.1
Requires (Dev)
- ext-simplexml: *
- php-coveralls/php-coveralls: ^2.4
- phpstan/phpstan: ^0.12.66
- phpstan/phpstan-phpunit: ^0.12.17
- phpunit/phpunit: ^9.5
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.5
This package is auto-updated.
Last update: 2024-09-24 15:32:32 UTC
README
优雅的货币实现
这个库提供了使用纯面向对象方法进行货币操作的类。
为什么又是PHP-money?
为什么我们又要制作另一个PHP的货币库?毕竟,已经有了
但是,在使用纯面向对象开发时存在一些缺点。
- 没有接口——我们无法创建一个表示货币本身的对象。
- 所有现有的实现都是具有数百行代码和数十个方法(如
add
、sub
、divide
、compare
等)的类,这些方法本质上属于过程式风格。 - 因为没有接口,所以我们无法添加新功能,并且大多数实现都是最终的。
- 急切执行——所有计算都在构造时发生,因为构造函数的参数应该被评估。
- 现有库隐藏了刻度,我们唯一能控制的是舍入方法。
我们的解决方案
- 我们有
Money
接口!这种实现不是过程式DTO。 - 没有数百行代码的类和数十个方法。每个操作都是一个实现了
Money
接口的对象。 - 延迟执行:您可以构建复杂的表达式而无需立即计算。
- 显式刻度以避免模糊的结果。
接口的存在允许您创建不同的类。例如,您可以轻松实现UserBalance
,它可以从数据库或某些API或甚至从文件中获取数据。
<?php declare(strict_types=1); use ElegantBro\Money\Currencies\USD; use ElegantBro\Money\Currency; use ElegantBro\Money\Money; final class UserBalance implements Money { /** * @var string */ private $userId; /** * @var PDO */ private $pdo; public function __construct(string $userId, PDO $pdo) { $this->userId = $userId; $this->pdo = $pdo; } public function amount(): string { $stmt = $this->pdo->prepare('SELECT SUM(debit - credit) FROM balances WHERE user_id = ?'); $stmt->execute([$this->userId]); return $stmt->fetchColumn(); } public function currency(): Currency { return new USD(); } public function scale(): int { return 2; } }
此外,您可以创建一些静态实现,如TwoDollars
<?php declare(strict_types=1); use ElegantBro\Money\Currencies\USD; use ElegantBro\Money\Currency; use ElegantBro\Money\Money; final class TwoDollars implements Money { public function amount(): string { return '2'; } public function currency(): Currency { return new USD(); } public function scale(): int { return 2; } }
您可以在任何开箱即用或自定义操作中使用这些对象。让我们看一个例子:您需要一个税,该税为给定金额的10%,但不低于2美元。
这是一个真正的面向对象解决方案
<?php declare(strict_types=1); use ElegantBro\Money\ArrayLot; use ElegantBro\Money\Money; use ElegantBro\Money\Operations\MaxOf; use ElegantBro\Money\Operations\Multiplied; use ElegantBro\Money\Wrapper; final class Tax extends Wrapper { public function __construct(Money $origin, Money $minTax) { $this->is( new MaxOf( new ArrayLot( Multiplied::keepScale($origin, '0.1'), $minTax ), $origin->scale() ) ); } } // on the client side $tax = new Tax( new UserBalance($uuid, $pdo), new TwoDollars() );
这是一段闪亮的声明性代码,但最大的优势是延迟。没有计算会在amount
方法被调用之前发生。
使用类似moneyphp/money
的过程式实现,您必须创建一些类似MoneyHelper
的东西,其中包含静态的tax
方法
<?php declare(strict_types=1); use Money/Money; class MoneyHelper { public static function tax(Money $amount, Money $minTax): Money { return Money::max( $amount->multiply('0.1'), $minTax ); } // usually helpers contain dozens of static methods } // on the client side $tax = MoneyHelper::tax( $userBalance, // you should fetch user balance from the database before Money::USD(200) );
很可能这个结果在后续的计算中并不需要。例如,存在某些免税条件,您应该在计算税之前检查它,以避免不必要的数据库请求并最终导致临时耦合。
安装
$ composer require elegant-bro/money
对于贡献者
在创建pull request之前,在本地运行所有测试。
构建测试容器并运行所有测试
make all
其他命令
# build the Dockerfile make build # install composer requirements make install # enter the container shell make shell # style check make style-check # run unit tests make unit # ensure coverage is 100% make coverage