Skip to content

Commit af944af

Browse files
committed
FileSize 1.0.0
1 parent 10b534c commit af944af

File tree

3 files changed

+289
-0
lines changed

3 files changed

+289
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Includes the following helpers:
99
- [Cally](https://github.com/nabeghe/cally-php) <small>v0.1.0</small>
1010
- [Colory](https://github.com/nabeghe/colory-php) <small>v0.1.0</small>
1111
- [Dati](https://github.com/nabeghe/dati-php) <small>v0.3.1</small>
12+
- [FileSize](https://github.com/nabeghe/file-size-php) <small>v1.0.0</small>
1213
- [HeadersReader](https://github.com/nabeghe/headers-reader-php) <small>v1.0.1</small>
1314
- [Levex](https://github.com/nabeghe/levex-php) <small>v1.0.1</small>
1415
- [Matcher](https://github.com/nabeghe/matcher-php)<small> v1.0.0</small>

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"Nabeghe\\Cally\\": "src/Cally",
2727
"Nabeghe\\Colory\\": "src/Colory",
2828
"Nabeghe\\Dati\\": "src/Dati",
29+
"Nabeghe\\FileSize\\": "src/FileSize",
2930
"Nabeghe\\HeadersReader\\": "src/HeadersReader",
3031
"Nabeghe\\Levex\\": "src/Levex",
3132
"Nabeghe\\Matcher\\": "src/Matcher",

src/FileSize/FileSize.php

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
<?php namespace Nabeghe\FileSize;
2+
3+
use InvalidArgumentException;
4+
5+
/**
6+
* Helper methods related to file size operations.
7+
*/
8+
class FileSize
9+
{
10+
/**
11+
* Returns all supported units.
12+
*
13+
* - bits: ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb']
14+
* - bytes: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
15+
*
16+
* @param bool $bits Return bit units instead of byte units.
17+
* @return array
18+
*/
19+
public static function getAllUnits(bool $bits = false): array
20+
{
21+
return $bits
22+
// bit, kilobit, megabit, gigabit, terabit, petabit, exabit, zettabit, yottabit
23+
? ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb']
24+
// byte, kilobyte, megabyte, gigabyte, terabyte, petabyte, exabyte, zettabyte, yottabyte
25+
: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
26+
}
27+
28+
/**
29+
* Checks whether a unit is valid or not.
30+
*
31+
* @param string $unit Unit value.
32+
* @return bool
33+
*/
34+
public static function isValidUnit(string $unit): bool
35+
{
36+
$units = [
37+
static::getAllUnits(),
38+
static::getAllUnits(true),
39+
];
40+
41+
$normalized_unit = ucfirst(strtolower($unit));
42+
43+
if (strtolower($unit) === 'b') {
44+
$normalized_unit = 'b';
45+
}
46+
47+
if (strtoupper($unit) === 'B' && strlen($unit) === 1) {
48+
$normalized_unit = 'B';
49+
}
50+
51+
return in_array($normalized_unit, $units, true);
52+
}
53+
54+
/**
55+
* Format a number with optional decimals, Returns integer if no decimals needed.
56+
*
57+
* @param float|int $size Number to format.
58+
* @param int $decimals Optional. Decimal places. Default 2.
59+
* @return int|float Formatted number as int or float.
60+
*/
61+
public static function format($size, int $decimals = 2)
62+
{
63+
if (is_int($size)) {
64+
return $size;
65+
}
66+
67+
$rounded = round($size, $decimals);
68+
$is_whole = ($rounded == (int) $rounded);
69+
70+
return $is_whole ? (int) $rounded : $rounded;
71+
}
72+
73+
/**
74+
* Converts a data size from one unit to another (supports bits and bytes).
75+
*
76+
* @param float|int $size The numeric value to convert.
77+
* @param string $fromUnit Source unit (e.g. "MB", "Gb", "B", "Kb").
78+
* @param string $toUnit Target unit to convert to.
79+
* @param bool|int $format Optional. Round and format result to given decimal places (true = 2 decimals). Default false.
80+
* @param bool|int $isBinary Optional. Whether to use binary (1024) or decimal (1000) base. Default true.
81+
* @return float|int Converted value (raw or formatted).
82+
* @throws InvalidArgumentException If either unit is invalid.
83+
*/
84+
public static function convert($size, string $fromUnit, string $toUnit, $format = false, $isBinary = true)
85+
{
86+
$units = [
87+
'b' => -1,
88+
'B' => 0,
89+
'KB' => 1,
90+
'MB' => 2,
91+
'GB' => 3,
92+
'TB' => 4,
93+
'PB' => 5,
94+
'EB' => 6,
95+
'ZB' => 7,
96+
'YB' => 8,
97+
'Kb' => 1,
98+
'Mb' => 2,
99+
'Gb' => 3,
100+
'Tb' => 4,
101+
'Pb' => 5,
102+
'Eb' => 6,
103+
'Zb' => 7,
104+
'Yb' => 8,
105+
];
106+
107+
if (!isset($units[$fromUnit])) {
108+
throw new InvalidArgumentException("Invalid fromUnit: $fromUnit");
109+
}
110+
111+
if (!isset($units[$toUnit])) {
112+
throw new InvalidArgumentException("Invalid toUnit: $toUnit");
113+
}
114+
115+
$is_bit = function ($unit) {
116+
$len = strlen($unit);
117+
return $len > 0 && substr($unit, -1) === 'b' && $unit !== 'B';
118+
};
119+
120+
$is_from_bit = $is_bit($fromUnit);
121+
$is_to_bit = $is_bit($toUnit);
122+
123+
// Get index values (powers of 1024)
124+
$from_index = $units[$fromUnit];
125+
$to_index = $units[$toUnit];
126+
127+
// Convert to bytes
128+
$bytes = $size;
129+
if ($is_from_bit) {
130+
$bytes = $size / 8; // Convert bits to bytes
131+
}
132+
if ($from_index > 0) {
133+
$bytes *= pow($isBinary ? 1024 : 1000, $from_index); // Convert from fromUnit to base bytes
134+
}
135+
136+
// Convert from bytes to target unit
137+
$result = $bytes;
138+
if ($to_index > 0) {
139+
$result /= pow($isBinary ? 1024 : 1000, $to_index); // Convert to toUnit
140+
}
141+
if ($is_to_bit) {
142+
$result *= 8; // Convert bytes to bits
143+
}
144+
145+
if ($format) {
146+
$result = static::format($result, $format === true ? 2 : $format);
147+
}
148+
149+
return $result;
150+
}
151+
152+
/**
153+
* Compares two data sizes across different units.
154+
*
155+
* @param float|int $size1 First value to compare.
156+
* @param string $unit1 Unit of the first value (e.g. "MB", "Gb").
157+
* @param float|int $size2 Second value to compare.
158+
* @param string $unit2 Unit of the second value.
159+
* @return int Returns -1 if size1 < size2, 1 if size1 > size2, or 0 if equal.
160+
*/
161+
public static function compare($size1, string $unit1, $size2, string $unit2): int
162+
{
163+
// Convert both values to bytes using convert method
164+
$bytes1 = static::convert($size1, $unit1, 'B');
165+
$bytes2 = static::convert($size2, $unit2, 'B');
166+
167+
if ($bytes1 < $bytes2) {
168+
return -1;
169+
} elseif ($bytes1 > $bytes2) {
170+
return 1;
171+
} else {
172+
return 0;
173+
}
174+
}
175+
176+
/**
177+
* Detects the most suitable unit for a given size (bytes or bits).
178+
*
179+
* @param float|int $size The size to evaluate.
180+
* @param bool $isBits Whether the size is in bits (default: false for bytes).
181+
* @param float|null $finalSize Outputs the normalized size after unit scaling.
182+
* @param bool|int $isBinary Optional. Whether to use binary (1024) or decimal (1000) base. Default true.
183+
* @return string The best-fit unit (e.g. "MB", "Gb").
184+
*/
185+
public static function detectUnit($size, bool $isBits = false, ?float &$finalSize = null, bool $isBinary = true): string
186+
{
187+
$units = $isBits ? ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb'] : ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
188+
189+
$divider = $isBinary ? 1024 : 1000;
190+
$unitIndex = 0;
191+
192+
// Normalize up (e.g. 2048 -> 2 KB)
193+
while ($size >= $divider && $unitIndex < count($units) - 1) {
194+
$size /= $divider;
195+
$unitIndex++;
196+
}
197+
198+
// Normalize down (e.g. 0.0005 GB -> 0.5 MB)
199+
while ($size < 1 && $unitIndex > 0) {
200+
$size *= $divider;
201+
$unitIndex--;
202+
}
203+
204+
$finalSize = $size;
205+
206+
return $units[$unitIndex];
207+
}
208+
209+
/**
210+
* Converts a size to a human-readable string with appropriate unit.
211+
*
212+
* @param float|int $size The size to format.
213+
* @param bool $isBits Whether to use bit units (default: false for bytes).
214+
* @param array|null $labels Optional map of custom labels for units.
215+
* @param bool|int $isBinary Optional. Whether to use binary (1024) or decimal (1000) base. Default true.
216+
* @return string Human-readable formatted string (e.g. "1.5 MB").
217+
*/
218+
public static function readable($size, bool $isBits = false, ?array $labels = [], bool $isBinary = true): string
219+
{
220+
$unit = static::detectUnit($size, $isBits, $finalSize, $isBinary);
221+
222+
if ($labels && isset($labels[$unit])) {
223+
$unit = $labels[$unit];
224+
}
225+
226+
return static::format($finalSize).' '.$unit;
227+
}
228+
229+
/**
230+
* Converts a size with unit to a human-readable string.
231+
*
232+
* @param float|int $size The numeric value to convert.
233+
* @param string $unit Unit of the value (e.g. "MB", "Kb").
234+
* @param array|null $labels Optional custom unit labels.
235+
* @param bool|int $isBinary Optional. Whether to use binary (1024) or decimal (1000) base. Default true.
236+
* @return string Human-readable formatted string.
237+
*/
238+
public static function readableFromUnit($size, string $unit, ?array $labels = [], bool $isBinary = true): string
239+
{
240+
$is_bits = strpos($unit, 'b');
241+
242+
$bytes = static::convert($size, $unit, $is_bits !== false ? 'b' : 'B', false, $isBinary);
243+
244+
return static::readable($bytes, $is_bits, $labels, $isBinary);
245+
}
246+
247+
/**
248+
* Parses a human-readable size string like "1.5 GB" or "200 Kb".
249+
*
250+
* @param string $input Human-readable size string.
251+
* @return array [$size, $unit].
252+
* @throws InvalidArgumentException If input format or unit is invalid.
253+
*/
254+
public static function parse(string $input): array
255+
{
256+
if (!preg_match('/^\s*([\d.]+)\s*([a-zA-Z]+)\s*$/', $input, $matches)) {
257+
throw new InvalidArgumentException("Invalid size string: $input");
258+
}
259+
260+
$size = (float) $matches[1];
261+
$unit = $matches[2];
262+
263+
if (!static::isValidUnit($unit)) {
264+
throw new InvalidArgumentException("Invalid unit in string: $unit");
265+
}
266+
267+
return [$size, $unit];
268+
}
269+
270+
/**
271+
* Calculates what percentage size1 is of size2.
272+
*
273+
* @param float|int $size1 First size value.
274+
* @param string $unit1 Unit of the first size.
275+
* @param float|int $size2 Second size value.
276+
* @param string $unit2 Unit of the second size.
277+
* @param bool|int $isBinary Optional. Whether to use binary (1024) or decimal (1000) base. Default true.
278+
* @return float Percentage value.
279+
*/
280+
public static function percentage($size1, string $unit1, $size2, string $unit2, bool $isBinary = true): float
281+
{
282+
$bytes1 = static::convert($size1, $unit1, 'B', false, $isBinary);
283+
$bytes2 = static::convert($size2, $unit2, 'B', false, $isBinary);
284+
285+
return ($bytes2 == 0) ? 0.0 : ($bytes1 / $bytes2) * 100;
286+
}
287+
}

0 commit comments

Comments
 (0)