Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.26% covered (warning)
78.26%
54 / 69
87.50% covered (warning)
87.50%
14 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
FloatInt
78.26% covered (warning)
78.26%
54 / 69
87.50% covered (warning)
87.50%
14 / 16
52.84
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 toInt
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 setLocalization
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setString
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAmount
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
7.18
 getFloat
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
56
 add
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getInt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sub
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 mult
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 div
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 abs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 pow
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 serialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 unserialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setInt
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Stdlib
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Stdlib\Base;
16
17use phpOMS\Contract\SerializableInterface;
18
19/**
20 * FloatInt class.
21 *
22 * @package phpOMS\Stdlib
23 * @license OMS License 2.0
24 * @link    https://jingga.app
25 * @since   1.0.0
26 */
27class FloatInt implements SerializableInterface
28{
29    /**
30     * Max amount of decimals.
31     *
32     * @var int
33     * @since 1.0.0
34     */
35    public const MAX_DECIMALS = 4;
36
37    /**
38     * Thousands separator.
39     *
40     * @var string
41     * @since 1.0.0
42     */
43    public string $thousands = ',';
44
45    /**
46     * Decimal separator.
47     *
48     * @var string
49     * @since 1.0.0
50     */
51    public string $decimal = '.';
52
53    /**
54     * Value.
55     *
56     * @var int
57     * @since 1.0.0
58     */
59    public int $value = 0;
60
61    /**
62     * Constructor.
63     *
64     * @param int|float|string $value     Value
65     * @param string           $thousands Thousands separator
66     * @param string           $decimal   Decimal separator
67     *
68     * @since 1.0.0
69     */
70    public function __construct(int | float | string $value = 0, string $thousands = ',', string $decimal = '.')
71    {
72        $this->value     = \is_int($value) ? $value : self::toInt((string) $value);
73        $this->thousands = $thousands;
74        $this->decimal   = $decimal;
75    }
76
77    /**
78     * FloatInt to int.
79     *
80     * @param string $value     FloatInt value
81     * @param string $thousands Thousands character
82     * @param string $decimal   Decimal character
83     *
84     * @return int
85     *
86     * @throws \Exception this exception is thrown if an internal explode or substr error occurs
87     *
88     * @since 1.0.0
89     */
90    public static function toInt(string $value, string $thousands = ',', string $decimal = '.') : int
91    {
92        $split = \explode($decimal, $value);
93
94        if ($split === false) {
95            throw new \Exception('Internal explode error.'); // @codeCoverageIgnore
96        }
97
98        $left  = $split[0];
99        $left  = \str_replace($thousands, '', $left);
100        $right = '';
101
102        if (\count($split) > 1) {
103            $right = $split[1];
104        }
105
106        $right = \substr($right, 0, self::MAX_DECIMALS);
107        if ($right === false) {
108            throw new \Exception('Internal substr error.'); // @codeCoverageIgnore
109        }
110
111        return ((int) $left) * 10 ** self::MAX_DECIMALS + (int) \str_pad($right, self::MAX_DECIMALS, '0');
112    }
113
114    /**
115     * Set localization.
116     *
117     * @param string $thousands Thousands separator
118     * @param string $decimal   Decimal separator
119     *
120     * @return FloatInt
121     *
122     * @since 1.0.0
123     */
124    public function setLocalization(string $thousands = ',', string $decimal = '.') : self
125    {
126        $this->thousands = $thousands;
127        $this->decimal   = $decimal;
128
129        return $this;
130    }
131
132    /**
133     * Set value by string.
134     *
135     * @param string $value FloatInt value
136     *
137     * @return FloatInt
138     *
139     * @since 1.0.0
140     */
141    public function setString(string $value) : self
142    {
143        $this->value = self::toInt($value, $this->thousands, $this->decimal);
144
145        return $this;
146    }
147
148    /**
149     * Get money.
150     *
151     * @param null|int $decimals Precision (null = auto decimals)
152     *
153     * @return string
154     *
155     * @throws \Exception this exception is thrown if an internal substr error occurs
156     *
157     * @since 1.0.0
158     */
159    public function getAmount(?int $decimals = 2) : string
160    {
161        $isNegative = $this->value < 0 ? 1 : 0;
162
163        $value = $this->value === 0
164            ? \str_repeat('0', self::MAX_DECIMALS)
165            : (string) \round($this->value, -self::MAX_DECIMALS + $decimals);
166
167        $left = \substr($value, 0, -self::MAX_DECIMALS + $isNegative);
168
169        /** @var string $left */
170        $left  = $left === false ? '0' : $left;
171        $right = \substr($value, -self::MAX_DECIMALS + $isNegative);
172
173        if ($right === false) {
174            throw new \Exception(); // @codeCoverageIgnore
175        }
176
177        if ($decimals === null) {
178            $decimals = \strlen(\rtrim($right, '0'));
179        }
180
181        return $decimals > 0
182            ? \number_format((float) $left, 0, $this->decimal, $this->thousands) . $this->decimal . \substr($right, 0, $decimals)
183            : \str_pad($left, 1, '0');
184    }
185
186    /**
187     * Get money.
188     *
189     * @param null|int $decimals Precision (null = auto decimals)
190     *
191     * @return string
192     *
193     * @throws \Exception this exception is thrown if an internal substr error occurs
194     *
195     * @since 1.0.0
196     */
197    public function getFloat(?int $decimals = 2) : string
198    {
199        $isNegative = $this->value < 0 ? 1 : 0;
200
201        $value = $this->value === 0
202            ? \str_repeat('0', self::MAX_DECIMALS)
203            : (string) \round($this->value, -self::MAX_DECIMALS + $decimals);
204
205        $left = \substr($value, 0, -self::MAX_DECIMALS + $isNegative);
206
207        /** @var string $left */
208        $left  = $left === false ? '0' : $left;
209        $right = \substr($value, -self::MAX_DECIMALS + $isNegative);
210
211        if ($right === false) {
212            throw new \Exception(); // @codeCoverageIgnore
213        }
214
215        if ($decimals === null) {
216            $decimals = \strlen(\rtrim($right, '0'));
217        }
218
219        return $decimals > 0
220            ? \number_format((float) $left, 0, $this->decimal, '') . $this->decimal . \substr($right, 0, $decimals)
221            : \str_pad($left, 1, '0');
222    }
223
224    /**
225     * Add money.
226     *
227     * @param int|float|string|FloatInt $value Value to add
228     *
229     * @return FloatInt
230     *
231     * @since 1.0.0
232     */
233    public function add(int | float | string | self $value) : self
234    {
235        if (\is_string($value) || \is_float($value)) {
236            $this->value += self::toInt((string) $value, $this->thousands, $this->decimal);
237        } elseif (\is_int($value)) {
238            $this->value += $value;
239        } else {
240            $this->value += $value->getInt();
241        }
242
243        return $this;
244    }
245
246    /**
247     * Get money value.
248     *
249     * @return int
250     *
251     * @since 1.0.0
252     */
253    public function getInt() : int
254    {
255        return $this->value;
256    }
257
258    /**
259     * Sub money.
260     *
261     * @param int|float|string|FloatInt $value Value to subtract
262     *
263     * @return FloatInt
264     *
265     * @since 1.0.0
266     */
267    public function sub(int | float | string | self $value) : self
268    {
269        if (\is_string($value) || \is_float($value)) {
270            $this->value -= self::toInt((string) $value, $this->thousands, $this->decimal);
271        } elseif (\is_int($value)) {
272            $this->value -= $value;
273        } else {
274            $this->value -= $value->getInt();
275        }
276
277        return $this;
278    }
279
280    /**
281     * Mult.
282     *
283     * @param int|float $value Value to multiply with
284     *
285     * @return FloatInt
286     *
287     * @since 1.0.0
288     */
289    public function mult(int | float $value) : self
290    {
291        $this->value = (int) ($this->value * $value);
292
293        return $this;
294    }
295
296    /**
297     * Div.
298     *
299     * @param int|float $value Value to divide by
300     *
301     * @return FloatInt
302     *
303     * @since 1.0.0
304     */
305    public function div(int | float $value) : self
306    {
307        $this->value = (int) ($this->value / $value);
308
309        return $this;
310    }
311
312    /**
313     * Abs.
314     *
315     * @return FloatInt
316     *
317     * @since 1.0.0
318     */
319    public function abs() : self
320    {
321        $this->value = \abs($this->value);
322
323        return $this;
324    }
325
326    /**
327     * Power.
328     *
329     * @param int|float $value Value to power
330     *
331     * @return FloatInt
332     *
333     * @since 1.0.0
334     */
335    public function pow(int | float $value) : self
336    {
337        $this->value = (int) ($this->value ** $value);
338
339        return $this;
340    }
341
342    /**
343     * Searialze.
344     *
345     * @return string
346     *
347     * @since 1.0.0
348     */
349    public function serialize() : string
350    {
351        return (string) $this->getInt();
352    }
353
354    /**
355     * Unserialize.
356     *
357     * @param int|string $value Value to unserialize
358     *
359     * @return void
360     *
361     * @since 1.0.0
362     */
363    public function unserialize(mixed $value) : void
364    {
365        $this->setInt((int) $value);
366    }
367
368    /**
369     * Set money value.
370     *
371     * @param int $value Value
372     *
373     * @return FloatInt
374     *
375     * @since 1.0.0
376     */
377    public function setInt(int $value) : self
378    {
379        $this->value = $value;
380
381        return $this;
382    }
383}