mistralys / currency-parser
PHP 库,可以检测以文本或标记形式编写的价格,并按照特定国家的规则规范化它们的格式。
Requires
- php: >=7.4
- mistralys/application-localization: ^1.4
- mistralys/application-utils: ^2.4
- mistralys/mailcode: ^3.2
Requires (Dev)
- phpstan/phpstan: >=1.10
- phpunit/phpunit: >=9.6
- roave/security-advisories: dev-latest
README
PHP 库,可以检测以文本或标记形式编写的价格,添加非断行空格,并按照特定国家的规则规范化它们的格式。
要求
- PHP 7.4+
- Composer
安装
使用 Composer 将库添加到您的项目中
composer require mistralys/currency-parser
或者,使用 GIT 命令行(或 GitHub Desktop)本地克隆,或手动 下载发行版。
已识别格式的示例
主要用于解析人类编写的价格,解析器相当宽容,将识别以下任何表示法(以及更多) - 要么作为独立文本,要么散布在更大的文档中。
$1000
$1,000.00
带有千位分隔符$ 1 , 000 . 00
自由间距,包括换行符$1.000,00
分隔符样式无关$1.000.00
相同的分隔符错误*$1000.2
1 到 2 位小数1000 EUR
货币符号或名称EUR 1000
符号位置无关-$ 1000
符号前减号$ -1000
符号后减号50,- €
德式简短小数点1 000,00 € TTC
法式带增值税.50 €
仅小数
- 基于假设价格始终有 1-2 位小数,该数字部分被认为是小数。
欧元 异常
只有 欧元 是支持货币中名称以与货币名称相同的字母开头的,即 EUR
。解析器将忽略以下这样的价格
"...and he gave him 42 Euro for his troubles."
这种名称在纯货币环境中是模糊的。它通常用于在正文中提及价格,其中不需要或甚至期望价格格式化。
快速入门
获取文本或标记中的价格列表
use function Mistralys\CurrencyParser\findPrices; $subject = <<<EOT Base price: $1,000.00 Your price: $860.00 EOT; $prices = findPrices($subject, 'USD'); foreach($prices as $price) { // do something with them }
格式化单个价格字符串
对所有支持货币使用自动检测
use function Mistralys\CurrencyParser\parsePrice; echo parsePrice('$1000.00')->formatText();
输出
$1,000.00
对于在多个国家使用货币(如欧元)的情况,使用特定的货币区域设置进行精确格式化
use function Mistralys\CurrencyParser\parsePrice; echo parsePrice('1000.00 €', 'EUR_DE')->formatText();
输出
1.000,00 €
格式化文本或标记中的所有价格
以下内容将自动根据所选货币的默认区域设置格式化价格。
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' Starting price: 1000.00 $ Special price: € 860.00 EOT; echo PriceFilter::createForLocales('USD', 'EUR') ->filterString($subject);
输出
Starting price: $1,000.00
Special price: 860.00 €
更准确地说,可以提供货币区域设置。例如,法国的欧元格式与欧洲其他地区的格式略有不同。
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' Prix de départ: EUR 1000.00 Prix spécial: EUR 860.00 EOT; echo PriceFilter::createForLocales('EUR_FR') ->filterString($subject);
输出
Prix de départ: 1 000,00 EUR
Prix spécial: 860,00 EUR
添加非断行空格到价格
在格式化价格时,自动将空格替换为非断行空格字符,以便在文本文档中使用。这可以轻松切换到 HTML 上下文。
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' Starting price: 1000.00 $ Special price: 860.00 $ EOT; echo PriceFilter::createForLocales('USD') ->setNonBreakingSpaceHTML() ->filterString($subject)
输出
Prix de départ: 1 000,00 EUR
Prix spécial: 860,00 EUR
更改货币符号
默认情况下,货币过滤器不会更改文档中使用的货币符号。这意味着即使格式化数字后,混合符号的使用也将保持不变。
更改为货币符号
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' With name: USD 1000 With symbol: $ 1000 EOT; echo PriceFilter::createForLocales('USD') ->setSymbolModeSymbol() ->filterString($subject);
输出
With name: $1,000
With symbol: $1,000
更改为货币名称
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' With name: USD 1000 With symbol: $ 1000 EOT; echo PriceFilter::createForLocales('USD') ->setSymbolModeSymbol() ->filterString($subject);
输出
With name: $1,000
With symbol: $1,000
更改为国家首选样式
对于欧元,建议使用货币符号而不是名称来表示价格。
use Mistralys\CurrencyParser\PriceFilter; $subject = <<<'EOT' With name: EUR 1000 With symbol: € 1000 EOT; echo PriceFilter::createForLocales('EUR_FR') ->setSymbolModePreferred() ->filterString($subject);
输出
With name: € 1 000
With symbol: € 1 000
格式化器使用
什么是格式化器?
格式化器用于根据解析器在文本中找到的单独价格进行格式化。它知道如何根据捆绑的货币区域设置来格式化价格,例如美国或墨西哥美元,或法国欧元。它也可以根据您的喜好进行自定义以格式化价格。
注意:要一次性格式化多个价格,请查看价格过滤器。
基于区域的格式化
使用货币区域定义进行“点火并忘记”的格式化。
use Mistralys\CurrencyParser\PriceFormatter; use function Mistralys\CurrencyParser\parsePrice; echo PriceFormatter::createLocale('USD') ->format(parsePrice('$ 1000'))
区域格式化器不允许更改格式化细节,如小数分隔符。只能调整符号模式和空格字符。
use Mistralys\CurrencyParser\PriceFormatter; use function Mistralys\CurrencyParser\parsePrice; echo PriceFormatter::createLocale('USD') ->setNonBreakingSpaceHTML() ->setSymbolModeName() ->format(parsePrice('$ 1000'))
自定义格式化
使用自定义格式化器,可以自由调整所有格式化细节。
use Mistralys\CurrencyParser\PriceFormatter; use function Mistralys\CurrencyParser\parsePrice; $formatter = PriceFormatter::createCustom() ->setDecimalSeparator('[DECIMAL]') ->setThousandsSeparator('[THOUSAND]') ->setArithmeticSeparator('[ARITHMETIC]') ->setNonBreakingSpace('[SPACE]') ->setSymbolPosition(PriceFormatter::SYMBOL_POSITION_END) ->setSymbolSpaceAtTheEnd(PriceFormatter::SPACE_BEFORE); echo $formatter->formatPrice(parsePrice('$ -1000.00'));
输出
-[ARITHMETIC]1[THOUSAND]000[DECIMAL]00[SPACE]$
注意:格式化器实例可以根据需要重复使用。
基于区域的自定义格式化器
假设我们希望使用默认的美元格式化,但不是将符号放在开头(默认行为),而是希望在末尾显示。
我们必须使用自定义格式化器来实现这一点,但我们可以使用现有的区域格式化器来填充默认设置。接下来要做的只是覆盖相关设置。
use Mistralys\CurrencyParser\PriceFormatter; $formatter = PriceFormatter::createCustom() ->configureWithLocale(\Mistralys\CurrencyParser\currencyLocale('USD')) ->setSymbolPositionAtTheEnd();
过滤器使用
什么是价格过滤器?
过滤器用于在文本或标记文档中以最小的代码量格式化多个价格,并保持文档的其他部分不变。
过滤文本文档
过滤文档的“点火并忘记”版本是指定要期望的货币类型,并让过滤器根据货币通常的格式处理所有细节。
use Mistralys\CurrencyParser\PriceFilter; $formatted = PriceFilter::createForLocales('USD') ->filterString($subject);
过滤HTML文档
这与文本文档的工作方式完全相同,除了调整非断行空格字符以使用HTML样式(使用HTML实体而不是实际字符)。
use Mistralys\CurrencyParser\PriceFilter; $formatted = PriceFilter::createForLocales('USD') ->setNonBreakingSpaceHTML() ->filterString($subject);
使用自定义格式化器
要为货币使用自定义格式化器而不是基于区域的格式化器,必须单独设置格式化器实例。
use Mistralys\CurrencyParser\PriceFilter; use Mistralys\CurrencyParser\PriceFormatter; // Configure a custom formatter $customFormatter = PriceFormatter::createCustom() ->setDecimalSeparator(' ') ->setThousandsSeparator(',') ->setSymbolPositionBeforeMinus() ->setSymbolModeName(); $formatted = PriceFilter::create() ->setFormatter('USD', $customFormatter) ->filterString($subject);
自定义格式化器和区域格式化器可以自由组合。在上面的示例中,我们使用了PriceFilter::create()
,因为所有格式化器都是自定义的。这里,我们使用默认的区域格式化器来处理法语价格,并使用自定义格式化器来处理美元。
use Mistralys\CurrencyParser\PriceFilter; use Mistralys\CurrencyParser\PriceFormatter; // Configure a custom formatter $customFormatter = PriceFormatter::createCustom() ->setDecimalSeparator(' ') ->setThousandsSeparator(',') ->setSymbolPositionBeforeMinus() ->setSymbolModeName(); $formatted = PriceFilter::createForLocales('EUR_FR') ->setFormatter('USD', $customFormatter) ->filterString($subject);
解析器使用
什么是解析器?
解析器能够在任意文本中找到价格,包括在标记(HTML或XML)中。它可以作为一个独立工具来访问价格信息,并按您的需要进行操作。
检测特定货币
要能够检测价格,解析器需要知道文档中期望的货币类型。在以下示例中,我们只使用欧元价格。
use Mistralys\CurrencyParser\PriceParser; $subject = 'Price of a basic subscription: 50,42 €.'; $prices = PriceParser::create() ->expectCurrency('EUR') ->findPrices($subject);
检测所有货币
出于性能原因,最好将要在文档中搜索的货币列表限制为最小。如果无法可靠地确定,可以使用所有货币。
use Mistralys\CurrencyParser\PriceParser; $subject = '(document with prices here)'; $prices = PriceParser::create() ->expectAnyCurrency() ->findPrices($subject);
有关如何处理具有相同货币符号的货币的下一节。
每个符号可能有多个可能的货币
可以为解析器添加多个货币。然而,如果它们具有相同的符号,它将无法区分它们。考虑以下文本
use Mistralys\CurrencyParser\PriceParser; $subject = <<<EOT Canadian dollars: $1,000 U.S. dollars: $1,000 EOT; $prices = PriceParser::create() ->expectCurrency('USD') ->expectCurrency('CAD') ->findPrices($subject); echo $prices[0]->getCurrencyName(); // USD echo $prices[1]->getCurrencyName(); // USD
解析器将无法区分这两个价格中的哪一个是加元(CAD)和美元(USD)。默认情况下,在发生与$
符号冲突时,解析器将使用USD
。使用货币名称则不存在这个问题。
use Mistralys\CurrencyParser\PriceParser; $subject = <<<EOT Canadian dollars: CAD1,000 U.S. dollars: USD1,000 EOT; $prices = PriceParser::create() ->expectCurrency('USD') ->expectCurrency('CAD') ->findPrices($subject); echo $prices[0]->getCurrencyName(); // CAD echo $prices[1]->getCurrencyName(); // USD
为每个符号设置默认货币
解析器具有内置的默认货币符号冲突设置,例如$
对应于USD
。但是,如果知道文档中使用的目标货币,则可以进行调整。考虑以下示例
use Mistralys\CurrencyParser\PriceParser; $subject = <<<EOT Starting price: $35 Black Friday rebate: $9.99 Your price: $25.01 EOT; $prices = PriceParser::create() ->expectCurrency('CAD') ->setSymbolDefault('$', 'CAD') ->findPrices($subject); echo $prices[0]->getCurrencyName(); // CAD echo $prices[1]->getCurrencyName(); // CAD echo $prices[3]->getCurrencyName(); // CAD
如果文档使用相同的货币符号,将无法区分它们。只有使用货币名称才能解决此类情况。
邮编兼容性
Mailcode 命令预处理器在保护命令以应用格式化过滤器时使用数字作为占位符(更多关于这样做的原因)。这可能导致变量命令的占位符被错误地识别为价格。
带有变量命令的示例文本
Price from a variable: {showvar: $FOO.PRICE} EUR
showvar
命令通过安全功能转换为数字占位符
Price from a variable: 9990000000001999 EUR
为了避免将其格式化为价格(从而破坏 Mailcode 命令),只需通过 expectMailcode()
方法在货币解析器中启用 Mailcode 支持。
use Mailcode\Mailcode; use Mistralys\CurrencyParser\PriceParser; use Mistralys\CurrencyParser\PriceFilter; $text = 'Price from a variable: {showvar: $FOO.PRICE} EUR'; // Replace Mailcode commands with placeholders $safeguard = Mailcode::create()->createSafeguard($text); $safeText = $safeguard->makeSafe(); $currencyParser = PriceParser::create() ->expectCurrency('EUR') ->expectMailcode(); // Format all currencies in the text $formattedText = PriceFilter::create($currencyParser) ->filterString($safeText); // Restore the Mailcode commands $filteredText = $safeguard->makeWhole($formattedText);
注意:这仅在默认 Mailcode 占位符分隔符下工作。不支持使用自定义分隔符。
处理多货币文档
在包含多种货币的文档中,如果它们使用相同的符号(例如 USD 和 CAD),则必须将其中一个设置为默认值。
哲学
PHP 有一个出色的货币库,Money。这个库并不试图重现相同的功能——它是为特定应用程序开发的,需要完全注意空格的格式化。一般方法侧重于过滤方面,而 Money 是一个完整的金融计算工具。
这两个库可以一起使用:价格有一个方法可以获取它们的货币整数风格值。
// (int)5000 $money = \Mistralys\CurrencyParser\parsePrice('$50')->getAsMoney();
注意:内置的基于位置的格式化可能与 Money 这样的库略有不同。这是由于库开发的应用程序中定义的格式化规则造成的。
贡献
欢迎贡献。该库目前并不旨在包含所有全球货币,但我们欢迎您通过拉取请求添加的任何货币。查看 Currencies 文件夹以了解概况。