amount = $amount; $this->maxDecimalPlaces = $maxDecimalPlaces; $this->minAmount = $minAmount; $this->maxAmount = $maxAmount; } /** * 执行验证 */ public function validate(): bool { // 验证基本格式 if (!$this->validateFormat()) { return false; } // 验证数值范围 if (!$this->validateRange()) { return false; } // 验证小数位数 if (!$this->validateDecimalPlaces()) { return false; } // 验证特殊值 if (!$this->validateSpecialValues()) { return false; } return true; } /** * 验证基本格式 */ private function validateFormat(): bool { // 检查是否为空 if (empty($this->amount)) { $this->addError('金额不能为空'); return false; } // 检查是否为数字格式 if (!is_numeric($this->amount)) { $this->addError('金额格式无效'); return false; } // 检查是否包含非法字符 if (!preg_match('/^-?\d+(\.\d+)?$/', $this->amount)) { $this->addError('金额只能包含数字和小数点'); return false; } return true; } /** * 验证数值范围 */ private function validateRange(): bool { // 检查是否为负数 if (bccomp($this->amount, '0', $this->maxDecimalPlaces) < 0) { $this->addError('金额不能为负数'); return false; } // 检查最小值 if (bccomp($this->amount, $this->minAmount, $this->maxDecimalPlaces) < 0) { $this->addError("金额不能小于 {$this->minAmount}"); return false; } // 检查最大值 if (bccomp($this->amount, $this->maxAmount, $this->maxDecimalPlaces) > 0) { $this->addError("金额不能大于 {$this->maxAmount}"); return false; } return true; } /** * 验证小数位数 */ private function validateDecimalPlaces(): bool { // 检查小数位数 $parts = explode('.', $this->amount); if (count($parts) > 2) { $this->addError('金额格式无效'); return false; } if (count($parts) === 2) { $decimalPart = $parts[1]; if (strlen($decimalPart) > $this->maxDecimalPlaces) { $this->addError("金额小数位数不能超过 {$this->maxDecimalPlaces} 位"); return false; } // 检查小数部分是否全为0(如果是,建议使用整数格式) if (preg_match('/^0+$/', $decimalPart)) { // 这不是错误,但可以优化 } } return true; } /** * 验证特殊值 */ private function validateSpecialValues(): bool { // 检查是否为0 if (bccomp($this->amount, '0', $this->maxDecimalPlaces) === 0) { $this->addError('金额不能为0'); return false; } // 检查是否为无穷大或NaN if (!is_finite((float) $this->amount)) { $this->addError('金额值无效'); return false; } // 检查科学计数法 if (strpos(strtolower($this->amount), 'e') !== false) { $this->addError('金额不支持科学计数法格式'); return false; } return true; } /** * 格式化金额 */ public function format(): string { // 移除前导零和尾随零 $formatted = rtrim(rtrim($this->amount, '0'), '.'); // 如果结果为空,返回0 if (empty($formatted) || $formatted === '.') { return '0'; } return $formatted; } /** * 转换为标准格式 */ public function toStandardFormat(): string { return number_format((float) $this->amount, $this->maxDecimalPlaces, '.', ''); } /** * 验证金额是否足够支付手续费 */ public function validateWithFee(string $feeAmount): bool { if (bccomp($this->amount, $feeAmount, $this->maxDecimalPlaces) <= 0) { $this->addError("金额必须大于手续费 {$feeAmount}"); return false; } return true; } /** * 静态验证方法 */ public static function isValid(string $amount, int $maxDecimalPlaces = 10): bool { $validator = new self($amount, $maxDecimalPlaces); return $validator->validate(); } /** * 比较两个金额 */ public static function compare(string $amount1, string $amount2, int $precision = 10): int { return bccomp($amount1, $amount2, $precision); } /** * 金额加法 */ public static function add(string $amount1, string $amount2, int $precision = 10): string { return bcadd($amount1, $amount2, $precision); } /** * 金额减法 */ public static function subtract(string $amount1, string $amount2, int $precision = 10): string { return bcsub($amount1, $amount2, $precision); } /** * 金额乘法 */ public static function multiply(string $amount, string $multiplier, int $precision = 10): string { return bcmul($amount, $multiplier, $precision); } /** * 金额除法 */ public static function divide(string $amount, string $divisor, int $precision = 10): string { if (bccomp($divisor, '0', $precision) === 0) { throw new \InvalidArgumentException('除数不能为0'); } return bcdiv($amount, $divisor, $precision); } }