Completed
Push — master ( 031221...6cba54 )
by Marcel
02:18
created

Bytes::setPrecision()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
namespace nochso\Omni\Format;
3
4
use nochso\Omni\Numeric;
5
6
/**
7
 * Bytes formats a quantity of bytes using different suffixes and binary or decimal base.
8
 *
9
 * By default a binary base and IEC suffixes are used:
10
 *
11
 * ```php
12
 * Bytes::create()->format(1100); // '1.07 KiB'
13
 * ```
14
 *
15
 * You can pick a base and suffix with `create()` or use the specifc setter methods.
16
 */
17
class Bytes
18
{
19
    /**
20
     * 1024 binary base.
21
     */
22
    const BASE_BINARY = 1024;
23
    /**
24
     * 1000 decimal base.
25
     */
26
    const BASE_DECIMAL = 1000;
27
    /**
28
     * B, M, G, ...
29
     */
30
    const SUFFIX_SIMPLE = 0;
31
    /**
32
     * KiB, MiB, GiB, ... (SHOULD be used with BASE_BINARY).
33
     */
34
    const SUFFIX_IEC = 1;
35
    /**
36
     * kibibytes, mebibytes, gibibytes, ... (SHOULD be used with BASE_BINARY).
37
     */
38
    const SUFFIX_IEC_LONG = 2;
39
    /**
40
     * kB, MB, GB, ... (SHOULD be used with BASE_DECIMAL).
41
     */
42
    const SUFFIX_SI = 3;
43
    /**
44
     * kilobytes, megabytes, gigabytes, ... (SHOULD be used with BASE_DECIMAL).
45
     */
46
    const SUFFIX_SI_LONG = 4;
47
48
    private static $suffixes = [
49
        self::SUFFIX_SIMPLE => ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
50
        self::SUFFIX_IEC => ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'],
51
        self::SUFFIX_IEC_LONG => ['byte(s)', 'kibibyte(s)', 'mebibyte(s)', 'gibibyte(s)', 'tebibyte(s)', 'pebibyte(s)', 'exbibyte(s)', 'zebibyte(s)', 'yobibyte(s)'],
52
        self::SUFFIX_SI => ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
53
        self::SUFFIX_SI_LONG => ['byte(s)', 'kilobyte(s)', 'megabyte(s)', 'gigabyte(s)', 'terabyte(s)', 'petabyte(s)', 'exabyte(s)', 'zettabyte(s)', 'yottabyte(s)'],
54
    ];
55
56
    /**
57
     * @var int
58
     */
59
    private $base = self::BASE_BINARY;
60
    /**
61
     * @var int
62
     */
63
    private $suffix = self::SUFFIX_IEC;
64
    /**
65
     * @var int
66
     */
67
    private $precision = 2;
68
    /**
69
     * @var bool
70
     */
71
    private $precisionTrimming = true;
72
73
    /**
74
     * Create a new Bytes instance.
75
     *
76
     * @param int $base   The base to use when converting to different units. Must be one of the `Bytes::BASE_*`
77
     *                    constants. Optional, defaults to `BASE_BINARY`.
78
     * @param int $suffix The suffix style for units. Must be one of the `Bytes::SUFFIX_*` constants. Optional,
79
     *                    defaults to SUFFIX_IEC (KiB, MiB, etc.)
80
     *
81
     * @return \nochso\Omni\Bytes
82
     */
83
    public static function create($base = self::BASE_BINARY, $suffix = self::SUFFIX_IEC)
84
    {
85
        $bytes = new self();
86
        $bytes->setBase($base)
87
            ->setSuffix($suffix);
88
        return $bytes;
89
    }
90
91
    /**
92
     * setBase to use when converting to different units.
93
     *
94
     * @param int $base Must be one of the `Bytes::BASE_*` constants.
95
     *
96
     * @return $this
97
     */
98
    public function setBase($base)
99
    {
100
        if ($base !== self::BASE_BINARY && $base !== self::BASE_DECIMAL) {
101
            throw new \InvalidArgumentException('Unknown base. Use either Bytes::BASE_BINARY or Bytes::BASE_DECIMAL');
102
        }
103
        $this->base = $base;
104
        return $this;
105
    }
106
107
    /**
108
     * setSuffix style for units.
109
     *
110
     * @param int $suffix Must be one of the `Bytes::SUFFIX_*` constants.
111
     *
112
     * @return $this
113
     */
114
    public function setSuffix($suffix)
115
    {
116
        if (!isset(self::$suffixes[$suffix])) {
117
            throw new \InvalidArgumentException('Unknown suffix. Use one of the Bytes::SUFFIX_* constants.');
118
        }
119
        $this->suffix = $suffix;
120
        return $this;
121
    }
122
123
    /**
124
     * setPrecision of floating point values after the decimal point.
125
     *
126
     * @param int $precision Any non-negative integer.
127
     *
128
     * @return $this
129
     */
130
    public function setPrecision($precision)
131
    {
132
        $this->precision = Numeric::ensureInteger($precision);
133
        return $this;
134
    }
135
136
    /**
137
     * enablePrecisionTrimming to remove trailing zeroes and decimal points.
138
     *
139
     * @return $this
140
     */
141
    public function enablePrecisionTrimming()
142
    {
143
        $this->precisionTrimming = true;
144
        return $this;
145
    }
146
147
    /**
148
     * disablePrecisionTrimming to keep trailing zeroes.
149
     *
150
     * @return $this
151
     */
152
    public function disablePrecisionTrimming()
153
    {
154
        $this->precisionTrimming = false;
155
        return $this;
156
    }
157
158
    /**
159
     * Format a quantity of bytes for human consumption.
160
     *
161
     * @param int $bytes
162
     *
163
     * @return string
164
     */
165
    public function format($bytes)
166
    {
167
        $bytes = Numeric::ensure($bytes);
168
        if (is_float($bytes) && $bytes > 0.0 && $bytes < 1.0) {
169
            throw new \InvalidArgumentException('Floats smaller than one can not be formatted.');
170
        }
171
        // 0 bytes won't work with log(), so set defaults for this case
172
        $exponent = 0;
173
        $normBytes = 0;
174
        if ($bytes !== 0) {
175
            $exponent = log(abs($bytes), $this->base);
176
            $normBytes = pow($this->base, $exponent - floor($exponent));
177
            // Make bytes negative again if needed
178
            $normBytes *= $bytes >= 0 ? 1 : -1;
179
        }
180
        $suffix = self::$suffixes[$this->suffix][$exponent];
181
        $number = number_format($normBytes, $this->precision, '.', '');
182
        $number = $this->trimPrecision($number);
183
        $suffix = Quantity::format($suffix, $number);
184
        return sprintf('%s %s', $number, $suffix);
185
    }
186
187
    /**
188
     * @param string $number
189
     *
190
     * @return float
191
     */
192
    private function trimPrecision($number)
193
    {
194
        if ($this->precisionTrimming && strpos((string) $number, '.') !== false) {
195
            $number = rtrim($number, '0');
196
            $number = rtrim($number, '.');
197
            $number = (double) $number;
198
        }
199
        return $number;
200
    }
201
}
202