allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Money\Parser;
use Money\Currency;
use Money\Exception;
use Money\Money;
use Money\MoneyParser;
use function sprintf;
/**
* Parses a string into a Money object using other parsers.
*/
final class AggregateMoneyParser implements MoneyParser
{
/**
* @param MoneyParser[] $parsers
* @phpstan-param non-empty-array<MoneyParser> $parsers
*/
public function __construct(private readonly array $parsers)
{
}
public function parse(string $money, Currency|null $fallbackCurrency = null): Money
{
foreach ($this->parsers as $parser) {
try {
return $parser->parse($money, $fallbackCurrency);
} catch (Exception\ParserException) {
}
}
throw new Exception\ParserException(sprintf('Unable to parse %s', $money));
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Money\Parser;
use Money\Currencies\BitcoinCurrencies;
use Money\Currency;
use Money\Exception\ParserException;
use Money\Money;
use Money\MoneyParser;
use function ltrim;
use function rtrim;
use function str_pad;
use function str_replace;
use function strlen;
use function strpos;
use function substr;
/**
* Parses Bitcoin currency to Money.
*/
final class BitcoinMoneyParser implements MoneyParser
{
public function __construct(private readonly int $fractionDigits)
{
}
public function parse(string $money, Currency|null $fallbackCurrency = null): Money
{
if (strpos($money, BitcoinCurrencies::SYMBOL) === false) {
throw new ParserException('Value cannot be parsed as Bitcoin');
}
$currency = $fallbackCurrency ?? new Currency(BitcoinCurrencies::CODE);
$decimal = str_replace(BitcoinCurrencies::SYMBOL, '', $money);
$decimalSeparator = strpos($decimal, '.');
if ($decimalSeparator !== false) {
$decimal = rtrim($decimal, '0');
$lengthDecimal = strlen($decimal);
$decimal = str_replace('.', '', $decimal);
$decimal .= str_pad('', ($lengthDecimal - $decimalSeparator - $this->fractionDigits - 1) * -1, '0');
} else {
$decimal .= str_pad('', $this->fractionDigits, '0');
}
if (substr($decimal, 0, 1) === '-') {
$decimal = '-' . ltrim(substr($decimal, 1), '0');
} else {
$decimal = ltrim($decimal, '0');
}
if ($decimal === '') {
$decimal = '0';
}
return new Money($decimal, $currency);
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Money\Parser;
use Money\Currencies;
use Money\Currency;
use Money\Exception\ParserException;
use Money\Money;
use Money\MoneyParser;
use Money\Number;
use function ltrim;
use function preg_match;
use function sprintf;
use function str_pad;
use function strlen;
use function substr;
use function trim;
/**
* Parses a decimal string into a Money object.
*/
final class DecimalMoneyParser implements MoneyParser
{
public const DECIMAL_PATTERN = '/^(?P<sign>-)?(?P<digits>\d+)?\.?(?P<fraction>\d+)?$/';
public function __construct(private readonly Currencies $currencies)
{
}
public function parse(string $money, Currency|null $fallbackCurrency = null): Money
{
if ($fallbackCurrency === null) {
throw new ParserException('DecimalMoneyParser cannot parse currency symbols. Use fallbackCurrency argument');
}
$decimal = trim($money);
if ($decimal === '') {
return new Money(0, $fallbackCurrency);
}
if (! preg_match(self::DECIMAL_PATTERN, $decimal, $matches) || ! isset($matches['digits'])) {
throw new ParserException(sprintf('Cannot parse "%s" to Money.', $decimal));
}
$negative = isset($matches['sign']) && $matches['sign'] === '-';
$decimal = $matches['digits'];
if ($negative) {
$decimal = '-' . $decimal;
}
$subunit = $this->currencies->subunitFor($fallbackCurrency);
if (isset($matches['fraction'])) {
$fractionDigits = strlen($matches['fraction']);
$decimal .= $matches['fraction'];
$decimal = Number::roundMoneyValue($decimal, $subunit, $fractionDigits);
if ($fractionDigits > $subunit) {
$decimal = substr($decimal, 0, $subunit - $fractionDigits);
} elseif ($fractionDigits < $subunit) {
$decimal .= str_pad('', $subunit - $fractionDigits, '0');
}
} else {
$decimal .= str_pad('', $subunit, '0');
}
if ($negative) {
$decimal = '-' . ltrim(substr($decimal, 1), '0');
} else {
$decimal = ltrim($decimal, '0');
}
if ($decimal === '' || $decimal === '-') {
$decimal = '0';
}
return new Money($decimal, $fallbackCurrency);
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Money\Parser;
use Money\Currencies;
use Money\Currency;
use Money\Exception\ParserException;
use Money\Money;
use Money\MoneyParser;
use Money\Number;
use NumberFormatter;
use function ltrim;
use function str_pad;
use function str_replace;
use function strlen;
use function strpos;
use function substr;
/**
* Parses a string into a Money object using intl extension.
*/
final class IntlLocalizedDecimalParser implements MoneyParser
{
public function __construct(private readonly NumberFormatter $formatter, private readonly Currencies $currencies)
{
}
public function parse(string $money, Currency|null $fallbackCurrency = null): Money
{
if ($fallbackCurrency === null) {
throw new ParserException('IntlLocalizedDecimalParser cannot parse currency symbols. Use forceCurrency argument');
}
$decimal = $this->formatter->parse($money);
if ($decimal === false) {
throw new ParserException('Cannot parse ' . $money . ' to Money. ' . $this->formatter->getErrorMessage());
}
$decimal = (string) $decimal;
$subunit = $this->currencies->subunitFor($fallbackCurrency);
$decimalPosition = strpos($decimal, '.');
if ($decimalPosition !== false) {
$decimalLength = strlen($decimal);
$fractionDigits = $decimalLength - $decimalPosition - 1;
$decimal = str_replace('.', '', $decimal);
$decimal = Number::roundMoneyValue($decimal, $subunit, $fractionDigits);
if ($fractionDigits > $subunit) {
$decimal = substr($decimal, 0, $decimalPosition + $subunit);
} elseif ($fractionDigits < $subunit) {
$decimal .= str_pad('', $subunit - $fractionDigits, '0');
}
} else {
$decimal .= str_pad('', $subunit, '0');
}
if ($decimal[0] === '-') {
$decimal = '-' . ltrim(substr($decimal, 1), '0');
} else {
$decimal = ltrim($decimal, '0');
}
if ($decimal === '') {
$decimal = '0';
}
return new Money($decimal, $fallbackCurrency);
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Money\Parser;
use Money\Currencies;
use Money\Currency;
use Money\Exception\ParserException;
use Money\Money;
use Money\MoneyParser;
use Money\Number;
use NumberFormatter;
use function assert;
use function ltrim;
use function str_pad;
use function str_replace;
use function strlen;
use function strpos;
use function substr;
/**
* Parses a string into a Money object using intl extension.
*/
final class IntlMoneyParser implements MoneyParser
{
public function __construct(private readonly NumberFormatter $formatter, private readonly Currencies $currencies)
{
}
public function parse(string $money, Currency|null $fallbackCurrency = null): Money
{
$currency = '';
// phpcs:ignore
/** @var string|float|bool|null $decimal */
$decimal = $this->formatter->parseCurrency($money, $currency);
if ($decimal === false || $decimal === null) {
throw new ParserException('Cannot parse ' . $money . ' to Money. ' . $this->formatter->getErrorMessage());
}
if ($fallbackCurrency === null) {
assert($currency !== '');
$fallbackCurrency = new Currency($currency);
}
$decimal = (string) $decimal;
$subunit = $this->currencies->subunitFor($fallbackCurrency);
$decimalPosition = strpos($decimal, '.');
if ($decimalPosition !== false) {
$decimalLength = strlen($decimal);
$fractionDigits = $decimalLength - $decimalPosition - 1;
$decimal = str_replace('.', '', $decimal);
$decimal = Number::roundMoneyValue($decimal, $subunit, $fractionDigits);
if ($fractionDigits > $subunit) {
$decimal = substr($decimal, 0, $decimalPosition + $subunit);
} elseif ($fractionDigits < $subunit) {
$decimal .= str_pad('', $subunit - $fractionDigits, '0');
}
} else {
$decimal .= str_pad('', $subunit, '0');
}
if ($decimal[0] === '-') {
$decimal = '-' . ltrim(substr($decimal, 1), '0');
} else {
$decimal = ltrim($decimal, '0');
}
if ($decimal === '') {
$decimal = '0';
}
return new Money($decimal, $fallbackCurrency);
}
}