Passed
Push — master ( 5b74be...84d61d )
by Eric
18:51 queued 06:03
created

Numbers   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
dl 0
loc 135
ccs 25
cts 25
cp 1
rs 10
c 1
b 0
f 0
wmc 13
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Utility - Collection of various PHP utility functions.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 * @version   2.0.0
10
 * @copyright (C) 2017 - 2024 Eric Sizemore
11
 * @license   The MIT License (MIT)
12
 *
13
 * Copyright (C) 2017 - 2024 Eric Sizemore <https://www.secondversion.com>.
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a copy
16
 * of this software and associated documentation files (the "Software"), to
17
 * deal in the Software without restriction, including without limitation the
18
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
19
 * sell copies of the Software, and to permit persons to whom the Software is
20
 * furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in
23
 * all copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
 * THE SOFTWARE.
32
 */
33
34
namespace Esi\Utility;
35
36
// Exceptions
37
use Random\RandomException;
38
use ValueError;
39
use InvalidArgumentException;
40
41
// Functions
42
use function number_format;
43
use function abs;
44
use function random_int;
45
use function sprintf;
46
use function count;
47
48
/**
49
 * Number utilities.
50
 */
51
final class Numbers
52
{
53
    /**
54
     * Constants for Numbers::sizeFormat(). Sets bases and modifier for the conversion.
55
     *
56
     * @var int   BINARY_STANDARD_BASE
57
     * @var int   METRIC_STANDARD_BASE
58
     * @var float CONVERSION_MODIFIER
59
     */
60
    public const BINARY_STANDARD_BASE = 1_024;
1 ignored issue
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ';' on line 60 at column 41
Loading history...
61
    public const METRIC_STANDARD_BASE = 1_000;
62
    public const CONVERSION_MODIFIER  = 0.9;
63
64
    /**
65
     * Ordinal suffixes.
66
     *
67
     * @var array<string> SUFFIXES
68
     */
69
    public const SUFFIXES = ['th', 'st', 'nd', 'rd'];
70
71
    /**
72
     * Standards units.
73
     *
74
     * @var array<string, array<string>> SIZE_FORMAT_UNITS
75
     */
76
    public const SIZE_FORMAT_UNITS = [
77
        'binary' => ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'],
78
        'metric' => ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
79
    ];
80
81
    /**
82
     * inside()
83
     *
84
     * Determines if a number is inside the min and max.
85
     *
86
     * @param   float|int  $number  The number to check.
87
     * @param   float|int  $min     The minimum.
88
     * @param   float|int  $max     The maximum.
89
     */
90 2
    public static function inside(float | int $number, float | int $min, float | int $max): bool
91
    {
92 2
        return ($number >= $min && $number <= $max);
93
    }
94
95
    /**
96
     * outside()
97
     *
98
     * Determines if a number is outside the min and max.
99
     *
100
     * @param   float|int  $number  The number to check.
101
     * @param   float|int  $min     The minimum.
102
     * @param   float|int  $max     The maximum.
103
     */
104 2
    public static function outside(float | int $number, float | int $min, float | int $max): bool
105
    {
106 2
        return ($number < $min || $number > $max);
107
    }
108
109
    /**
110
     * random()
111
     *
112
     * Generate a cryptographically secure pseudo-random integer.
113
     *
114
     * @param   int<min, max>  $min  The lowest value to be returned, which must be PHP_INT_MIN or higher.
115
     * @param   int<min, max>  $max  The highest value to be returned, which must be less than or equal to PHP_INT_MAX.
116
     * @return  int<min, max>
117
     *
118
     * @throws RandomException | ValueError
119
     */
120 1
    public static function random(int $min, int $max): int
121
    {
122
        // Generate random int
123 1
        return random_int($min, $max);
124
    }
125
126
    /**
127
     * ordinal()
128
     *
129
     * Retrieve the ordinal version of a number.
130
     *
131
     * Basically, it will append th, st, nd, or rd based on what the number ends with.
132
     *
133
     * @param   int     $number  The number to create an ordinal version of.
134
     */
135 1
    public static function ordinal(int $number): string
136
    {
137 1
        static $suffixes = self::SUFFIXES;
138
139 1
        $absNumber = abs($number);
140
141 1
        $suffix = $absNumber % 100 >= 11 && $absNumber % 100 <= 13 ? $suffixes[0] : $suffixes[$absNumber % 10] ?? $suffixes[0];
142 1
        return $number . $suffix;
143
    }
144
145
    /**
146
     * sizeFormat()
147
     *
148
     * Format bytes to a human-readable format.
149
     *
150
     * @param   int     $bytes      The number in bytes.
151
     * @param   int     $precision  Sets the number of decimal digits.
152
     * @param   string  $standard   Determines which mod ('base') to use in the conversion.
153
     */
154 3
    public static function sizeFormat(int $bytes, int $precision = 0, string $standard = 'binary'): string
155
    {
156
        // The units/labels for each 'system'
157 3
        static $standards = [
158 3
            'binary' => ['base' => Numbers::BINARY_STANDARD_BASE, 'units' => self::SIZE_FORMAT_UNITS['binary']],
159 3
            'metric' => ['base' => Numbers::METRIC_STANDARD_BASE, 'units' => self::SIZE_FORMAT_UNITS['metric']],
160 3
        ];
161
162
        // Just a sanity check
163 3
        if (!isset($standards[$standard])) {
164 1
            throw new InvalidArgumentException('Invalid $standard specified, must be either metric or binary');
165
        }
166
167
        // Metric or Binary?
168 2
        $base  = $standards[$standard]['base'];
169 2
        $units = $standards[$standard]['units'];
170
171
        // If $bytes is less than our base, there is no need for any conversion
172 2
        if ($bytes < $base) {
173 2
            return sprintf('%s %s', $bytes, $units[0]);
174
        }
175
176
        // Perform the conversion
177 2
        for ($i = 0; ($bytes / $base) > Numbers::CONVERSION_MODIFIER && ($i < count($units) - 1); $i++) {
178 2
            $bytes /= $base;
179
        }
180
        // @phpstan-ignore-next-line
181 2
        return number_format($bytes, $precision, '.', '') . ' ' . $units[$i];
182
    }
183
}
184