Another set of PHPDoc fixes
[civicrm-core.git] / Civi / Core / Format.php
1 <?php
2
3 namespace Civi\Core;
4
5 use Brick\Money\Currency;
6 use Brick\Money\Money;
7 use Brick\Math\RoundingMode;
8 use Civi;
9 use CRM_Core_Config;
10 use CRM_Core_I18n;
11 use CRM_Utils_Constant;
12 use NumberFormatter;
13 use Brick\Money\Context\AutoContext;
14
15 /**
16 * Class Paths
17 * @package Civi\Core
18 *
19 * This class provides standardised formatting
20 */
21 class Format {
22
23 /**
24 * Get formatted money
25 *
26 * @param string|int|float $amount
27 * @param string|null $currency
28 * Currency, defaults to site currency if not provided.
29 * @param string|null $locale
30 *
31 * @return string
32 *
33 * @noinspection PhpDocMissingThrowsInspection
34 * @noinspection PhpUnhandledExceptionInspection
35 */
36 public function money($amount, ?string $currency = NULL, ?string $locale = NULL): string {
37 // Empty value => empty string
38 if (is_null($amount) || $amount === '' || $amount === FALSE) {
39 return '';
40 }
41 // Verify the amount is a number or numeric string/object
42 elseif ($amount === TRUE || !is_numeric((string) $amount)) {
43 throw new \CRM_Core_Exception('Invalid value for type money');
44 }
45 if (!$currency) {
46 $currency = Civi::settings()->get('defaultCurrency');
47 }
48 if (!isset($locale)) {
49 $locale = CRM_Core_I18n::getLocale();
50 }
51 $money = Money::of($amount, $currency, NULL, RoundingMode::HALF_UP);
52 $formatter = $this->getMoneyFormatter($currency, $locale);
53 return $money->formatWith($formatter);
54 }
55
56 /**
57 * Get a formatted number.
58 *
59 * @param string|int|float|Money $amount
60 * Amount in a machine money format.
61 * @param string|null $locale
62 * @param array $attributes
63 * Additional values supported by NumberFormatter
64 * https://www.php.net/manual/en/class.numberformatter.php
65 * By default this will set it to round to 8 places and not
66 * add any padding.
67 *
68 * @return string
69 */
70 public function number($amount, ?string $locale = NULL, array $attributes = [
71 NumberFormatter::MIN_FRACTION_DIGITS => 0,
72 NumberFormatter::MAX_FRACTION_DIGITS => 8,
73 ]): string {
74 $formatter = $this->getMoneyFormatter(NULL, $locale, NumberFormatter::DECIMAL, $attributes);
75 return $formatter->format($amount);
76 }
77
78 /**
79 * Get a number formatted with rounding expectations derived from the currency.
80 *
81 * @param string|float|int $amount
82 * @param string $currency
83 * @param string|null $locale
84 *
85 * @return string
86 *
87 * @noinspection PhpDocMissingThrowsInspection
88 * @noinspection PhpUnhandledExceptionInspection
89 */
90 public function moneyNumber($amount, string $currency, $locale): string {
91 $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::DECIMAL);
92 $money = Money::of($amount, $currency, NULL, RoundingMode::HALF_UP);
93 return $money->formatWith($formatter);
94 }
95
96 /**
97 * Get a money value with formatting but not rounding.
98 *
99 * @param string|float|int $amount
100 * @param string|null $currency
101 * @param string|null $locale
102 *
103 * @return string
104 *
105 * @noinspection PhpDocMissingThrowsInspection
106 * @noinspection PhpUnhandledExceptionInspection
107 */
108 public function moneyLong($amount, ?string $currency, ?string $locale): string {
109 $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::CURRENCY, [
110 NumberFormatter::MAX_FRACTION_DIGITS => 9,
111 ]);
112 $money = Money::of($amount, $currency, new AutoContext());
113 return $money->formatWith($formatter);
114 }
115
116 /**
117 * Get a number with minimum decimal places based on the currency but no rounding.
118 *
119 * @param string|float|int $amount
120 * @param string|null $currency
121 * @param string|null $locale
122 *
123 * @return string
124 *
125 * @noinspection PhpDocMissingThrowsInspection
126 * @noinspection PhpUnhandledExceptionInspection
127 */
128 public function moneyNumberLong($amount, ?string $currency, ?string $locale): string {
129 $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::DECIMAL, [
130 NumberFormatter::MAX_FRACTION_DIGITS => 9,
131 ]);
132 $money = Money::of($amount, $currency, new AutoContext());
133 return $money->formatWith($formatter);
134 }
135
136 /**
137 * Should we use the configured thousand & decimal separators.
138 *
139 * The goal is to phase this into being FALSE - but for now
140 * we are looking at how to manage an 'opt in'
141 */
142 protected function isUseSeparatorSettings(): bool {
143 return !CRM_Utils_Constant::value('IGNORE_SEPARATOR_CONFIG');
144 }
145
146 /**
147 * Get the money formatter for when we are using configured thousand separators.
148 *
149 * Our intent is to phase out these settings in favour of deriving them from the locale.
150 *
151 * @param string|null $currency
152 * @param string|null $locale
153 * @param int $style
154 * See https://www.php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants
155 * @param array $attributes
156 * See https://www.php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute
157 *
158 * @return \NumberFormatter
159 *
160 * @noinspection PhpDocMissingThrowsInspection
161 * @noinspection PhpUnhandledExceptionInspection
162 */
163 public function getMoneyFormatter(?string $currency = NULL, ?string $locale = NULL, int $style = NumberFormatter::CURRENCY, array $attributes = []): NumberFormatter {
164 if (!$currency) {
165 $currency = Civi::settings()->get('defaultCurrency');
166 }
167 $cacheKey = __CLASS__ . $currency . '_' . $locale . '_' . $style . (!empty($attributes) ? md5(json_encode($attributes)) : '');
168 if (!isset(\Civi::$statics[$cacheKey])) {
169 $formatter = new NumberFormatter($locale, $style);
170
171 if (!isset($attributes[NumberFormatter::MIN_FRACTION_DIGITS])) {
172 $attributes[NumberFormatter::MIN_FRACTION_DIGITS] = Currency::of($currency)
173 ->getDefaultFractionDigits();
174 }
175 if (!isset($attributes[NumberFormatter::MAX_FRACTION_DIGITS])) {
176 $attributes[NumberFormatter::MAX_FRACTION_DIGITS] = Currency::of($currency)
177 ->getDefaultFractionDigits();
178 }
179
180 foreach ($attributes as $attribute => $value) {
181 $formatter->setAttribute($attribute, $value);
182 }
183 if ($locale === CRM_Core_I18n::getLocale() && $this->isUseSeparatorSettings()) {
184 $formatter->setSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, CRM_Core_Config::singleton()->monetaryThousandSeparator);
185 $formatter->setSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL, CRM_Core_Config::singleton()->monetaryDecimalPoint);
186 }
187 \Civi::$statics[$cacheKey] = $formatter;
188 }
189 return \Civi::$statics[$cacheKey];
190 }
191
192 }