Skip to content

Commit 7cc70f5

Browse files
committed
Decoupling options from the Formatter
1 parent 1e9f1d8 commit 7cc70f5

13 files changed

+383
-183
lines changed

phpstan.neon

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ includes:
66
- vendor/phpstan/phpstan-strict-rules/rules.neon
77
parameters:
88
reportUnmatchedIgnoredErrors: true
9+
checkGenericClassInNonGenericObjectType: false
910
level: max
1011
paths:
1112
- src

src/Configuration.php

+43-19
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44

55
namespace Bakame\Laravel\Intl;
66

7+
use Bakame\Laravel\Intl\Options\DateType;
8+
use Bakame\Laravel\Intl\Options\NumberStyle;
9+
use Bakame\Laravel\Intl\Options\TimeType;
10+
711
final class Configuration
812
{
913
/** @readonly */
10-
public int $dateType;
14+
public DateType $dateType;
1115
/** @readonly */
12-
public int $timeType;
16+
public TimeType $timeType;
1317
/** @readonly */
14-
public int $style;
18+
public NumberStyle $style;
1519
/** @readonly */
1620
public ?string $datePattern;
1721
/** @readonly */
@@ -38,14 +42,14 @@ final class Configuration
3842
* @param array<int, string> $symbolAttributes
3943
*/
4044
public function __construct(
41-
int $dateType,
42-
int $timeType,
43-
int $style,
44-
?string $datePattern = null,
45-
?string $numberPattern = null,
46-
array $attributes = [],
47-
array $textAttributes = [],
48-
array $symbolAttributes = []
45+
DateType $dateType,
46+
TimeType $timeType,
47+
NumberStyle $style,
48+
?string $datePattern = null,
49+
?string $numberPattern = null,
50+
array $attributes = [],
51+
array $textAttributes = [],
52+
array $symbolAttributes = []
4953
) {
5054
$this->dateType = $dateType;
5155
$this->timeType = $timeType;
@@ -62,23 +66,43 @@ public function __construct(
6266
* date:array{
6367
* dateType:int,
6468
* timeType:int,
65-
* pattern:?string
69+
* pattern?:?string
6670
* },
6771
* number:array{
6872
* style:int,
69-
* pattern:?string,
70-
* attributes:array<int, int|float>,
71-
* textAttributes:array<int,string>,
72-
* symbolAttributes:array<int,string>
73+
* pattern?:?string,
74+
* attributes?:array<int, int|float>,
75+
* textAttributes?:array<int,string>,
76+
* symbolAttributes?:array<int,string>
7377
* }
7478
* } $settings
7579
*/
7680
public static function fromApplication(array $settings): self
7781
{
82+
if (!array_key_exists('pattern', $settings['date'])) {
83+
$settings['date']['pattern'] = null;
84+
}
85+
86+
if (!array_key_exists('pattern', $settings['number'])) {
87+
$settings['number']['pattern'] = null;
88+
}
89+
90+
if (!array_key_exists('attributes', $settings['number'])) {
91+
$settings['number']['attributes'] = [];
92+
}
93+
94+
if (!array_key_exists('textAttributes', $settings['number'])) {
95+
$settings['number']['textAttributes'] = [];
96+
}
97+
98+
if (!array_key_exists('symbolAttributes', $settings['number'])) {
99+
$settings['number']['symbolAttributes'] = [];
100+
}
101+
78102
return new self(
79-
$settings['date']['dateType'],
80-
$settings['date']['timeType'],
81-
$settings['number']['style'],
103+
DateType::from($settings['date']['dateType']),
104+
TimeType::from($settings['date']['timeType']),
105+
NumberStyle::from($settings['number']['style']),
82106
$settings['date']['pattern'],
83107
$settings['number']['pattern'],
84108
$settings['number']['attributes'],

src/FailedFormatting.php

+6-27
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,9 @@ public static function dueToInvalidDate(Throwable $exception = null): self
2929
/**
3030
* @param array<string,mixed> $supported
3131
*/
32-
public static function dueToUnknownTimeFormat(string $format, array $supported): self
32+
public static function dueToUnknownOptions(string $name, string $format, array $supported): self
3333
{
34-
return new self('The time format "'.$format.'" does not exist, known formats are: "'.implode('", "', array_keys($supported)).'".');
35-
}
36-
37-
/**
38-
* @param array<string,mixed> $supported
39-
*/
40-
public static function dueToUnknownDateFormat(string $format, array $supported): self
41-
{
42-
return new self('The date format "'.$format.'" does not exist, known formats are: "'.implode('", "', array_keys($supported)).'".');
43-
}
44-
45-
/**
46-
* @param array<string,mixed> $supported
47-
*/
48-
public static function dueToUnknownStyle(string $style, array $supported): self
49-
{
50-
return new self('The style "'.$style.'" does not exist, known styles are: "'.implode('", "', array_keys($supported)).'".');
51-
}
52-
53-
/**
54-
* @param array<string,mixed> $supported
55-
*/
56-
public static function dueToUnknownNumberType(string $type, array $supported): self
57-
{
58-
return new self('The type "'.$type.'" does not exist, known types are: "'.implode('", "', array_keys($supported)).'".');
34+
return new self('The '.$name.' "'.$format.'" does not exist; expecting one value of : "'.implode('", "', array_keys($supported)).'".');
5935
}
6036

6137
/**
@@ -84,7 +60,10 @@ public static function dueToUnknownNumberFormatterPaddingPosition($value, array
8460
return new self('The number formatter padding position "'.$value.'" does not exist, known positions are: "'.implode('", "', array_keys($supported)).'".');
8561
}
8662

87-
public static function dueToInvalidNumberFormatterAttributeValue(string $name, string $value): self
63+
/**
64+
* @param int|float|string $value
65+
*/
66+
public static function dueToInvalidNumberFormatterAttributeValue(string $name, $value): self
8867
{
8968
return new self('The number formatter value for "'.$name.'" can not be a string: "'.$value.'"');
9069
}

src/Formatter.php

+16-116
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
namespace Bakame\Laravel\Intl;
66

7+
use Bakame\Laravel\Intl\Options\DateType;
8+
use Bakame\Laravel\Intl\Options\NumberAttribute;
9+
use Bakame\Laravel\Intl\Options\NumberStyle;
10+
use Bakame\Laravel\Intl\Options\NumberType;
11+
use Bakame\Laravel\Intl\Options\TimeType;
712
use DateTimeInterface;
813
use DateTimeZone;
914
use Exception;
@@ -19,71 +24,6 @@
1924

2025
final class Formatter
2126
{
22-
private const DATE_FORMATS = [
23-
'none' => IntlDateFormatter::NONE,
24-
'short' => IntlDateFormatter::SHORT,
25-
'medium' => IntlDateFormatter::MEDIUM,
26-
'long' => IntlDateFormatter::LONG,
27-
'full' => IntlDateFormatter::FULL,
28-
];
29-
30-
private const NUMBER_TYPES = [
31-
'default' => NumberFormatter::TYPE_DEFAULT,
32-
'int32' => NumberFormatter::TYPE_INT32,
33-
'int64' => NumberFormatter::TYPE_INT64,
34-
'double' => NumberFormatter::TYPE_DOUBLE,
35-
'currency' => NumberFormatter::TYPE_CURRENCY,
36-
];
37-
38-
private const NUMBER_STYLES = [
39-
'decimal' => NumberFormatter::DECIMAL,
40-
'currency' => NumberFormatter::CURRENCY,
41-
'percent' => NumberFormatter::PERCENT,
42-
'scientific' => NumberFormatter::SCIENTIFIC,
43-
'spellout' => NumberFormatter::SPELLOUT,
44-
'ordinal' => NumberFormatter::ORDINAL,
45-
'duration' => NumberFormatter::DURATION,
46-
];
47-
48-
private const NUMBER_ATTRIBUTES = [
49-
'grouping_used' => NumberFormatter::GROUPING_USED,
50-
'decimal_always_shown' => NumberFormatter::DECIMAL_ALWAYS_SHOWN,
51-
'max_integer_digit' => NumberFormatter::MAX_INTEGER_DIGITS,
52-
'min_integer_digit' => NumberFormatter::MIN_INTEGER_DIGITS,
53-
'integer_digit' => NumberFormatter::INTEGER_DIGITS,
54-
'max_fraction_digit' => NumberFormatter::MAX_FRACTION_DIGITS,
55-
'min_fraction_digit' => NumberFormatter::MIN_FRACTION_DIGITS,
56-
'fraction_digit' => NumberFormatter::FRACTION_DIGITS,
57-
'multiplier' => NumberFormatter::MULTIPLIER,
58-
'grouping_size' => NumberFormatter::GROUPING_SIZE,
59-
'rounding_mode' => NumberFormatter::ROUNDING_MODE,
60-
'rounding_increment' => NumberFormatter::ROUNDING_INCREMENT,
61-
'format_width' => NumberFormatter::FORMAT_WIDTH,
62-
'padding_position' => NumberFormatter::PADDING_POSITION,
63-
'secondary_grouping_size' => NumberFormatter::SECONDARY_GROUPING_SIZE,
64-
'significant_digits_used' => NumberFormatter::SIGNIFICANT_DIGITS_USED,
65-
'min_significant_digits_used' => NumberFormatter::MIN_SIGNIFICANT_DIGITS,
66-
'max_significant_digits_used' => NumberFormatter::MAX_SIGNIFICANT_DIGITS,
67-
'lenient_parse' => NumberFormatter::LENIENT_PARSE,
68-
];
69-
70-
private const NUMBER_ROUNDING_ATTRIBUTES = [
71-
'ceiling' => NumberFormatter::ROUND_CEILING,
72-
'floor' => NumberFormatter::ROUND_FLOOR,
73-
'down' => NumberFormatter::ROUND_DOWN,
74-
'up' => NumberFormatter::ROUND_UP,
75-
'halfeven' => NumberFormatter::ROUND_HALFEVEN,
76-
'halfdown' => NumberFormatter::ROUND_HALFDOWN,
77-
'halfup' => NumberFormatter::ROUND_HALFUP,
78-
];
79-
80-
private const NUMBER_PADDING_ATTRIBUTES = [
81-
'before_prefix' => NumberFormatter::PAD_BEFORE_PREFIX,
82-
'after_prefix' => NumberFormatter::PAD_AFTER_PREFIX,
83-
'before_suffix' => NumberFormatter::PAD_BEFORE_SUFFIX,
84-
'after_suffix' => NumberFormatter::PAD_AFTER_SUFFIX,
85-
];
86-
8727
private Configuration $configuration;
8828
private DateResolver $dateResolver;
8929
/** @var array<IntlDateFormatter> */
@@ -193,7 +133,7 @@ public function getCountryTimezones(string $country): array
193133
*/
194134
public function formatCurrency($amount, string $currency, array $attrs = [], string $locale = null): string
195135
{
196-
$formatter = $this->createNumberFormatter($locale, 'currency', $attrs);
136+
$formatter = $this->createNumberFormatter($locale, NumberStyle::fromName('currency'), $attrs);
197137
if (false === $ret = $formatter->formatCurrency($amount, $currency)) {
198138
// @codeCoverageIgnoreStart
199139
throw FailedFormatting::dueToNumberFormatter('Unable to format the given number as a currency.');
@@ -214,15 +154,9 @@ public function formatNumber(
214154
string $type = 'default',
215155
string $locale = null
216156
): string {
217-
if (!isset(self::NUMBER_TYPES[$type])) {
218-
throw FailedFormatting::dueToUnknownNumberType($type, self::NUMBER_TYPES);
219-
}
220-
221-
/** @var string $style */
222-
$style = $style ?? array_search($this->configuration->style, self::NUMBER_STYLES, true);
223-
157+
$style = null === $style ? $this->configuration->style : NumberStyle::fromName($style);
224158
$formatter = $this->createNumberFormatter($locale, $style, $attrs);
225-
if (false === $ret = $formatter->format($number, self::NUMBER_TYPES[$type])) {
159+
if (false === $ret = $formatter->format($number, NumberType::fromName($type)->value)) {
226160
// @codeCoverageIgnoreStart
227161
throw FailedFormatting::dueToNumberFormatter('Unable to format the given number.');
228162
// @codeCoverageIgnoreEnd
@@ -300,23 +234,15 @@ private function createDateFormatter(
300234
DateTimeZone $timezone,
301235
string $calendar
302236
): IntlDateFormatter {
303-
if (null !== $dateFormat && !isset(self::DATE_FORMATS[$dateFormat])) {
304-
throw FailedFormatting::dueToUnknownDateFormat($dateFormat, self::DATE_FORMATS);
305-
}
306-
307-
if (null !== $timeFormat && !isset(self::DATE_FORMATS[$timeFormat])) {
308-
throw FailedFormatting::dueToUnknownTimeFormat($timeFormat, self::DATE_FORMATS);
309-
}
310-
237+
$dateType = null !== $dateFormat ? DateType::fromName($dateFormat) : $this->configuration->dateType;
238+
$timeType = null !== $timeFormat ? TimeType::fromName($timeFormat) : $this->configuration->timeType;
311239
$locale = $locale ?? Locale::getDefault();
312240
$calendar = 'gregorian' === strtolower($calendar) ? IntlDateFormatter::GREGORIAN : IntlDateFormatter::TRADITIONAL;
313-
$dateFormatValue = self::DATE_FORMATS[$dateFormat] ?? $this->configuration->dateType;
314-
$timeFormatValue = self::DATE_FORMATS[$timeFormat] ?? $this->configuration->timeType;
315241
$pattern = $pattern ?? $this->configuration->datePattern;
316242

317-
$hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezone->getName().'|'.$calendar.'|'.$pattern;
243+
$hash = $locale.'|'.$dateType->value.'|'.$timeType->value.'|'.$timezone->getName().'|'.$calendar.'|'.$pattern;
318244
if (!isset($this->dateFormatters[$hash])) {
319-
$dateFormatter = new IntlDateFormatter($locale, $dateFormatValue, $timeFormatValue, $timezone, $calendar);
245+
$dateFormatter = new IntlDateFormatter($locale, $dateType->value, $timeType->value, $timezone, $calendar);
320246
if (null !== $pattern) {
321247
$dateFormatter->setPattern($pattern);
322248
}
@@ -329,18 +255,14 @@ private function createDateFormatter(
329255
/**
330256
* @param array<string, string|float|int> $attrs
331257
*/
332-
private function createNumberFormatter(?string $locale, string $style, array $attrs = []): NumberFormatter
258+
private function createNumberFormatter(?string $locale, NumberStyle $style, array $attrs = []): NumberFormatter
333259
{
334-
if (!isset(self::NUMBER_STYLES[$style])) {
335-
throw FailedFormatting::dueToUnknownStyle($style, self::NUMBER_STYLES);
336-
}
337-
338260
$locale = $locale ?? Locale::getDefault();
339261

340262
ksort($attrs);
341-
$hash = $locale.'|'.$style.'|'.json_encode($attrs);
263+
$hash = $locale.'|'.$style->value.'|'.json_encode($attrs);
342264
if (!isset($this->numberFormatters[$hash])) {
343-
$newNumberFormatter = new NumberFormatter($locale, self::NUMBER_STYLES[$style]);
265+
$newNumberFormatter = new NumberFormatter($locale, $style->value);
344266
$this->addDefaultAttributes($newNumberFormatter);
345267
$this->setNumberFormatterAttributes($newNumberFormatter, $attrs);
346268
$this->numberFormatters[$hash] = $newNumberFormatter;
@@ -355,29 +277,7 @@ private function createNumberFormatter(?string $locale, string $style, array $at
355277
private function setNumberFormatterAttributes(NumberFormatter $numberFormatter, array $attrs): void
356278
{
357279
foreach ($attrs as $name => $value) {
358-
if (!isset(self::NUMBER_ATTRIBUTES[$name])) {
359-
throw FailedFormatting::dueToUnknownNumberFormatterAttributeName($name, self::NUMBER_ATTRIBUTES);
360-
}
361-
362-
if ('rounding_mode' === $name) {
363-
if (!isset(self::NUMBER_ROUNDING_ATTRIBUTES[$value])) {
364-
throw FailedFormatting::dueToUnknownNumberFormatterRoundingMode($value, self::NUMBER_ROUNDING_ATTRIBUTES);
365-
}
366-
367-
$value = self::NUMBER_ROUNDING_ATTRIBUTES[$value];
368-
} elseif ('padding_position' === $name) {
369-
if (!isset(self::NUMBER_PADDING_ATTRIBUTES[$value])) {
370-
throw FailedFormatting::dueToUnknownNumberFormatterPaddingPosition($value, self::NUMBER_PADDING_ATTRIBUTES);
371-
}
372-
373-
$value = self::NUMBER_PADDING_ATTRIBUTES[$value];
374-
}
375-
376-
if (is_string($value)) {
377-
throw FailedFormatting::dueToInvalidNumberFormatterAttributeValue($name, $value);
378-
}
379-
380-
$numberFormatter->setAttribute(self::NUMBER_ATTRIBUTES[$name], $value);
280+
NumberAttribute::from($name, $value)->addTo($numberFormatter);
381281
}
382282
}
383283

0 commit comments

Comments
 (0)