Completed
Push — master ( 79000f...ecfa5f )
by Sébastien
02:41
created

setDisableUseOfNonBreakingSpaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * soluble-flexstore library
7
 *
8
 * @author    Vanvelthem Sébastien
9
 * @link      https://github.com/belgattitude/soluble-flexstore
10
 * @copyright Copyright (c) 2016-2017 Vanvelthem Sébastien
11
 * @license   MIT License https://github.com/belgattitude/soluble-flexstore/blob/master/LICENSE.md
12
 *
13
 */
14
15
namespace Soluble\FlexStore\Formatter;
16
17
use Soluble\FlexStore\Exception;
18
use Soluble\FlexStore\I18n\LocalizableInterface;
19
use ArrayObject;
20
use Locale;
21
use NumberFormatter as IntlNumberFormatter;
22
23
class NumberFormatter implements FormatterInterface, LocalizableInterface, FormatterNumberInterface
24
{
25
    public const NO_BREAK_SPACE_HEX = 'c2a0';
26
    public const NARROW_NO_BREAK_SPACE_HEX = 'e280af';
27
28
    /**
29
     * Formatter instances.
30
     *
31
     * @var array
32
     */
33
    protected $formatters = [];
34
35
    /**
36
     * @var array
37
     */
38
    protected $params = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $default_params = [
44
        'decimals' => 2,
45
        'locale' => null,
46
        'pattern' => null,
47
        'disableUseOfNonBreakingSpaces' => false
48
    ];
49
50
    /**
51
     * @param array $params
52
     *
53
     * @throws Exception\ExtensionNotLoadedException if ext/intl is not present
54
     * @throws Exception\InvalidArgumentException
55
     */
56 24
    public function __construct(array $params = [])
57
    {
58 24
        if (!extension_loaded('intl')) {
59
            throw new Exception\ExtensionNotLoadedException(sprintf(
60
                '%s component requires the intl PHP extension',
61
                __NAMESPACE__
62
            ));
63
        }
64
65
        // As default locale may include unsupported
66
        // variants (like 'en_US_POSIX' for example),
67
        // only the 5 chars will be taken into consideration
68
69 24
        $default_locale = Locale::getDefault();
70 24
        $this->default_params['locale'] = substr($default_locale, 0, 5);
71 24
        $this->setParams($params);
72 24
    }
73
74
    /**
75
     * @throws Exception\InvalidArgumentException
76
     *
77
     * @param array $params
78
     */
79 24
    protected function setParams($params)
80
    {
81 24
        $this->params = $this->default_params;
82 24
        foreach ($params as $name => $value) {
83 17
            $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name))));
84 17
            if (!method_exists($this, $method)) {
85 3
                throw new Exception\InvalidArgumentException(__METHOD__ . " Parameter '$name' does not exists.");
86
            }
87 14
            $this->$method($value);
88
        }
89 24
    }
90
91 12
    protected function initWhitespaceSeparator(IntlNumberFormatter $formatter): void
92
    {
93 12
        if ($this->params['disableUseOfNonBreakingSpaces'] === true
94 3
        && in_array(bin2hex($formatter->getSymbol(IntlNumberFormatter::GROUPING_SEPARATOR_SYMBOL)), [
95 3
                self::NARROW_NO_BREAK_SPACE_HEX,
96 3
                self::NO_BREAK_SPACE_HEX
97 12
            ], true)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
98
99 3
            $formatter->setSymbol(IntlNumberFormatter::GROUPING_SEPARATOR_SYMBOL, ' ');
100
        }
101 12
    }
102
103 6
    protected function loadFormatterId(string $formatterId): void
104
    {
105 6
        $locale = $this->params['locale'];
106 6
        $formatter = new IntlNumberFormatter(
107 6
            $locale,
108 6
            IntlNumberFormatter::DECIMAL
109
        );
110 6
        $formatter->setAttribute(IntlNumberFormatter::FRACTION_DIGITS, $this->params['decimals']);
111 6
        if ($this->params['pattern'] !== null) {
112 2
            $formatter->setPattern($this->params['pattern']);
113
        }
114
115 6
        $this->initWhitespaceSeparator($formatter);
116
117 6
        $this->formatters[$formatterId] = $formatter;
118 6
    }
119
120
    /**
121
     * Format a number.
122
     *
123
     * @param float $number
124
     *
125
     * @return string
126
     */
127 3
    public function format($number, ArrayObject $row = null): string
128
    {
129 3
        $locale = $this->params['locale'];
130
        //$formatterId = md5($locale);
131 3
        $formatterId = $locale . (string) $this->params['pattern'];
132 3
        if (!array_key_exists($formatterId, $this->formatters)) {
133 3
            $this->loadFormatterId($formatterId);
134
        }
135
136 3
        if (!is_numeric($number)) {
137 2
            $this->throwNumberFormatterException($this->formatters[$formatterId], $number);
138
        }
139
140 1
        return $this->formatters[$formatterId]->format($number);
141
    }
142
143
    /**
144
     * Throws an Exception when number cannot be formatted.
145
     *
146
     * @param IntlNumberFormatter $intlFormatter
147
     * @param int|string|float    $number
148
     *
149
     * @throws Exception\RuntimeException
150
     */
151 4
    protected function throwNumberFormatterException(IntlNumberFormatter $intlFormatter, $number): void
152
    {
153 4
        $error_code = $intlFormatter->getErrorCode();
154 4
        if (is_scalar($number)) {
155 2
            $val = (string) $number;
156
        } else {
157 2
            $val = 'type: ' . gettype($number);
158
        }
159 4
        throw new Exception\RuntimeException(__METHOD__ . " Cannot format value '$val', Intl/NumberFormatter error code: $error_code.");
160
    }
161
162
    /**
163
     * Set locale to use instead of the default.
164
     *
165
     * @param string $locale
166
     */
167 17
    public function setLocale(?string $locale): self
168
    {
169 17
        $this->params['locale'] = $locale;
170
171 17
        return $this;
172
    }
173
174
    /**
175
     * Get the locale to use.
176
     *
177
     * @return string|null
178
     */
179 6
    public function getLocale(): ?string
180
    {
181 6
        return $this->params['locale'];
182
    }
183
184
    /**
185
     * Set decimals.
186
     *
187
     * @param int $decimals
188
     */
189 15
    public function setDecimals($decimals): self
190
    {
191 15
        $this->params['decimals'] = $decimals;
192
193 15
        return $this;
194
    }
195
196
    /**
197
     * @return int
198
     */
199 6
    public function getDecimals(): int
200
    {
201 6
        return $this->params['decimals'];
202
    }
203
204
    /**
205
     * Set the number pattern, (#,##0.###, ....).
206
     *
207
     * @see http://php.net/manual/en/numberformatter.setpattern.php
208
     *
209
     * @param string $pattern
210
     */
211 8
    public function setPattern($pattern): self
212
    {
213 8
        $this->params['pattern'] = $pattern;
214
215 8
        return $this;
216
    }
217
218
    /**
219
     * Get the number pattern.
220
     *
221
     * @return string|null
222
     */
223 6
    public function getPattern(): ?string
224
    {
225 6
        return $this->params['pattern'];
226
    }
227
228 3
    public function setDisableUseOfNonBreakingSpaces(bool $disable=true): self {
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$disable" and equals sign; expected 1 but found 0
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$disable"; expected 1 but found 0
Loading history...
229 3
        $this->params['disableUseOfNonBreakingSpaces'] = $disable;
230 3
        return $this;
231
    }
232
}
233