Passed
Push — 6.0 ( e6360e...a5ddb0 )
by Olivier
01:38
created

NumberPattern::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 11
dl 0
loc 13
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace ICanBoogie\CLDR;
4
5
use function abs;
6
use function implode;
7
use function ltrim;
8
use function round;
9
use function str_pad;
10
use function str_split;
11
use function strlen;
12
use function strpos;
13
use function substr;
14
15
use const STR_PAD_LEFT;
16
17
/**
18
 * Representation of a number pattern.
19
 */
20
final class NumberPattern
21
{
22
    public static function from(string $pattern): NumberPattern
23
    {
24
        static $instances;
25
26
        return $instances[$pattern] ??= self::do_from($pattern);
27
    }
28
29
    private static function do_from(string $pattern): self
30
    {
31
        $parsed_pattern = NumberPatternParser::parse($pattern);
32
33
        return new self($pattern, ...$parsed_pattern);
34
    }
35
36
    /**
37
     * @param string $pattern
38
     * @param string $positive_prefix
39
     *     Prefix to a positive number.
40
     * @param string $positive_suffix
41
     *     Suffix to a positive number.
42
     * @param string $negative_prefix
43
     *     Prefix to a negative number.
44
     * @param string $negative_suffix
45
     *     Suffix to negative number.
46
     * @param int $multiplier
47
     *     100 for percent, 1000 for per mille.
48
     * @param int $decimal_digits
49
     *     The number of required digits after the decimal point.
50
     *     The string is padded with zeros if there aren't enough digits.
51
     *     `-1` means the decimal point should be dropped.
52
     * @param int $max_decimal_digits
53
     *     The maximum number of digits after the decimal point.
54
     *     Additional digits will be truncated.
55
     * @param int $integer_digits
56
     *     The number of required digits before the decimal point.
57
     *     The string is padded with zeros if there aren't enough digits.
58
     * @param int $group_size1
59
     *     The primary grouping size. `0` means no grouping.
60
     * @param int $group_size2
61
     *     The secondary grouping size. `0` means no secondary grouping.
62
     */
63
    private function __construct(
64
        public readonly string $pattern,
65
        public readonly string $positive_prefix,
66
        public readonly string $positive_suffix,
67
        public readonly string $negative_prefix,
68
        public readonly string $negative_suffix,
69
        public readonly int $multiplier,
70
        public readonly int $decimal_digits,
71
        public readonly int $max_decimal_digits,
72
        public readonly int $integer_digits,
73
        public readonly int $group_size1,
74
        public readonly int $group_size2
75
    ) {
76
    }
77
78
    public function __toString(): string
79
    {
80
        return $this->pattern;
81
    }
82
83
    /**
84
     * Parses a number according to the pattern and return its integer and decimal parts.
85
     *
86
     * @param float|int|numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment float|int|numeric-string at position 4 could not be parsed: Unknown type name 'numeric-string' at position 4 in float|int|numeric-string.
Loading history...
87
     *
88
     * @return array{ 0: int, 1: string}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{ at position 2 could not be parsed: the token is null at position 2.
Loading history...
89
     *     Where `0` is the integer part and `1` the decimal part.
90
     */
91
    public function parse_number(float|int|string $number): array
92
    {
93
        $number = abs($number * $this->multiplier);
94
95
        if ($this->max_decimal_digits >= 0) {
96
            $number = round($number, $this->max_decimal_digits);
97
        }
98
99
        $number = "$number";
100
        $pos = strpos($number, '.');
101
102
        if ($pos !== false) {
103
            return [ (int)substr($number, 0, $pos), substr($number, $pos + 1) ];
104
        }
105
106
        return [ (int)$number, '' ];
107
    }
108
109
    /**
110
     * Formats an integer according to a group pattern.
111
     */
112
    public function format_integer_with_group(int $integer, string $group_symbol): string
113
    {
114
        $integer = str_pad((string)$integer, $this->integer_digits, '0', STR_PAD_LEFT);
115
        $group_size1 = $this->group_size1;
116
117
        if ($group_size1 < 1 || strlen($integer) <= $this->group_size1) {
118
            return $integer;
119
        }
120
121
        $group_size2 = $this->group_size2;
122
123
        $str1 = substr($integer, 0, -$group_size1);
124
        $str2 = substr($integer, -$group_size1);
125
        $size = $group_size2 > 0 ? $group_size2 : $group_size1;
126
        $str1 = str_pad($str1, (int)((strlen($str1) + $size - 1) / $size) * $size, ' ', STR_PAD_LEFT);
127
128
        return ltrim(implode($group_symbol, str_split($str1, $size))) . $group_symbol . $str2;
0 ignored issues
show
Bug introduced by
It seems like str_split($str1, $size) can also be of type true; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
        return ltrim(implode($group_symbol, /** @scrutinizer ignore-type */ str_split($str1, $size))) . $group_symbol . $str2;
Loading history...
129
    }
130
131
    /**
132
     * Formats an integer with a decimal.
133
     *
134
     * @param int|string $integer
135
     *     An integer, or a formatted integer as returned by {@link format_integer_with_group}.
136
     */
137
    public function format_integer_with_decimal(int|string $integer, string $decimal, string $decimal_symbol): string
138
    {
139
        if ($decimal === '0') {
140
            $decimal = '';
141
        }
142
143
        if ($this->decimal_digits > strlen($decimal)) {
144
            $decimal = str_pad($decimal, $this->decimal_digits, '0');
145
        }
146
147
        if (strlen($decimal)) {
148
            $decimal = $decimal_symbol . $decimal;
149
        }
150
151
        return "$integer" . $decimal;
152
    }
153
}
154