Skip to content

Commit 455a559

Browse files
feat: Differentiate between kilobyte/kibibyte and megabyte/mebibyte (#9277)
1 parent 35c5784 commit 455a559

File tree

7 files changed

+190
-4
lines changed

7 files changed

+190
-4
lines changed

system/Files/File.php

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,39 @@ public function getSize()
7070
return $this->size ?? ($this->size = parent::getSize());
7171
}
7272

73+
/**
74+
* Retrieve the file size by unit, calculated in IEC standards with 1024 as base value.
75+
*
76+
* @phpstan-param positive-int $precision
77+
*/
78+
public function getSizeByBinaryUnit(FileSizeUnit $unit = FileSizeUnit::B, int $precision = 3): int|string
79+
{
80+
return $this->getSizeByUnitInternal(1024, $unit, $precision);
81+
}
82+
83+
/**
84+
* Retrieve the file size by unit, calculated in metric standards with 1000 as base value.
85+
*
86+
* @phpstan-param positive-int $precision
87+
*/
88+
public function getSizeByMetricUnit(FileSizeUnit $unit = FileSizeUnit::B, int $precision = 3): int|string
89+
{
90+
return $this->getSizeByUnitInternal(1000, $unit, $precision);
91+
}
92+
7393
/**
7494
* Retrieve the file size by unit.
7595
*
96+
* @deprecated 4.6.0 Use getSizeByBinaryUnit() or getSizeByMetricUnit() instead
97+
*
7698
* @return false|int|string
7799
*/
78100
public function getSizeByUnit(string $unit = 'b')
79101
{
80102
return match (strtolower($unit)) {
81-
'kb' => number_format($this->getSize() / 1024, 3),
82-
'mb' => number_format(($this->getSize() / 1024) / 1024, 3),
83-
default => $this->getSize(),
103+
'kb' => $this->getSizeByBinaryUnit(FileSizeUnit::KB),
104+
'mb' => $this->getSizeByBinaryUnit(FileSizeUnit::MB),
105+
default => $this->getSize()
84106
};
85107
}
86108

@@ -189,4 +211,17 @@ public function getDestination(string $destination, string $delimiter = '_', int
189211

190212
return $destination;
191213
}
214+
215+
private function getSizeByUnitInternal(int $fileSizeBase, FileSizeUnit $unit, int $precision): int|string
216+
{
217+
$exponent = $unit->value;
218+
$divider = $fileSizeBase ** $exponent;
219+
$size = $this->getSize() / $divider;
220+
221+
if ($unit !== FileSizeUnit::B) {
222+
$size = number_format($size, $precision);
223+
}
224+
225+
return $size;
226+
}
192227
}

system/Files/FileSizeUnit.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\Files;
15+
16+
use CodeIgniter\Exceptions\InvalidArgumentException;
17+
18+
enum FileSizeUnit: int
19+
{
20+
case B = 0;
21+
case KB = 1;
22+
case MB = 2;
23+
case GB = 3;
24+
case TB = 4;
25+
26+
/**
27+
* Allows the creation of a FileSizeUnit from Strings like "kb" or "mb"
28+
*
29+
* @throws InvalidArgumentException
30+
*/
31+
public static function fromString(string $unit): self
32+
{
33+
return match (strtolower($unit)) {
34+
'b' => self::B,
35+
'kb' => self::KB,
36+
'mb' => self::MB,
37+
'gb' => self::GB,
38+
'tb' => self::TB,
39+
default => throw new InvalidArgumentException("Invalid unit: {$unit}"),
40+
};
41+
}
42+
}

tests/system/Files/FileTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use CodeIgniter\Files\Exceptions\FileNotFoundException;
1717
use CodeIgniter\Test\CIUnitTestCase;
18+
use PHPUnit\Framework\Attributes\DataProvider;
1819
use PHPUnit\Framework\Attributes\Group;
1920
use ZipArchive;
2021

@@ -113,6 +114,38 @@ public function testGetSizeReturnsBytes(): void
113114
$this->assertSame($size, $file->getSizeByUnit('b'));
114115
}
115116

117+
#[DataProvider('provideGetSizeData')]
118+
public function testGetSizeBinary(FileSizeUnit $unit): void
119+
{
120+
$divider = 1024 ** $unit->value;
121+
$file = new File(SYSTEMPATH . 'Common.php');
122+
$size = number_format(filesize(SYSTEMPATH . 'Common.php') / $divider, 3);
123+
$this->assertSame($size, $file->getSizeByBinaryUnit($unit));
124+
}
125+
126+
public function testGetSizeBinaryBytes(): void
127+
{
128+
$file = new File(SYSTEMPATH . 'Common.php');
129+
$size = filesize(SYSTEMPATH . 'Common.php');
130+
$this->assertSame($size, $file->getSizeByBinaryUnit(FileSizeUnit::B));
131+
}
132+
133+
#[DataProvider('provideGetSizeData')]
134+
public function testGetSizeMetric(FileSizeUnit $unit): void
135+
{
136+
$divider = 1000 ** $unit->value;
137+
$file = new File(SYSTEMPATH . 'Common.php');
138+
$size = number_format(filesize(SYSTEMPATH . 'Common.php') / $divider, 3);
139+
$this->assertSame($size, $file->getSizeByMetricUnit($unit));
140+
}
141+
142+
public function testGetSizeMetricBytes(): void
143+
{
144+
$file = new File(SYSTEMPATH . 'Common.php');
145+
$size = filesize(SYSTEMPATH . 'Common.php');
146+
$this->assertSame($size, $file->getSizeByMetricUnit(FileSizeUnit::B));
147+
}
148+
116149
public function testThrowsExceptionIfNotAFile(): void
117150
{
118151
$this->expectException(FileNotFoundException::class);
@@ -135,4 +168,25 @@ public function testGetDestination(): void
135168
unlink(SYSTEMPATH . 'Common_Copy.php');
136169
unlink(SYSTEMPATH . 'Common_Copy_5.php');
137170
}
171+
172+
/**
173+
* @return array<string, array<int, FileSizeUnit>>
174+
*/
175+
public static function provideGetSizeData()
176+
{
177+
return [
178+
'returns KB binary' => [
179+
FileSizeUnit::KB,
180+
],
181+
'returns MB binary' => [
182+
FileSizeUnit::MB,
183+
],
184+
'returns GB binary' => [
185+
FileSizeUnit::GB,
186+
],
187+
'returns TB binary' => [
188+
FileSizeUnit::TB,
189+
],
190+
];
191+
}
138192
}

user_guide_src/source/changelogs/v4.6.0.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ Model
222222
Libraries
223223
=========
224224

225+
- **File:** Added ``getSizeByBinaryUnit()`` and ``getSizeByMetricUnit()`` to ``File`` class.
226+
See :ref:`File::getSizeByBinaryUnit() <file-get-size-by-binary-unit>` and :ref:`File::getSizeByMetricUnit() <file-get-size-by-metric-unit>`.
225227
- **FileCollection:** Added ``retainMultiplePatterns()`` to ``FileCollection`` class.
226228
See :ref:`FileCollection::retainMultiplePatterns() <file-collections-retain-multiple-patterns>`.
227229
- **Validation:** Added ``min_dims`` validation rule to ``FileRules`` class. See
@@ -286,6 +288,9 @@ Deprecations
286288
- The properties ``$arguments`` and ``$argumentsClass`` of ``Filters`` have
287289
been deprecated. No longer used.
288290
- The ``Filters::getArguments()`` method has been deprecated. No longer used.
291+
- **File:**
292+
- The function ``getSizeByUnit()`` of ``File`` has been deprecated.
293+
Use either ``getSizeByBinaryUnit()`` or ``getSizeByMetricUnit()`` instead.
289294

290295
**********
291296
Bugs Fixed

user_guide_src/source/libraries/files.rst

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,50 @@ A ``RuntimeException`` will be thrown if the file does not exist or an error occ
5656
getSizeByUnit()
5757
===============
5858

59+
.. deprecated:: 4.6.0
60+
5961
Returns the size of the file default in bytes. You can pass in either ``'kb'`` or ``'mb'`` as the first parameter to get
60-
the results in kilobytes or megabytes, respectively:
62+
the results in kibibytes or mebibytes, respectively:
6163

6264
.. literalinclude:: files/005.php
6365
:lines: 2-
6466

6567
A ``RuntimeException`` will be thrown if the file does not exist or an error occurs.
6668

69+
70+
.. _file-get-size-by-binary-unit:
71+
72+
getSizeByBinaryUnit()
73+
=====================
74+
75+
.. versionadded:: 4.6.0
76+
77+
Returns the size of the file default in bytes. You can pass in different FileSizeUnit values as the first parameter to get
78+
the results in kibibytes, mebibytes etc. respectively. You can pass in a precision value as the second parameter to define
79+
the amount of decimal places.
80+
81+
.. literalinclude:: files/017.php
82+
:lines: 4-
83+
84+
A ``RuntimeException`` will be thrown if the file does not exist or an error occurs.
85+
86+
87+
.. _file-get-size-by-metric-unit:
88+
89+
getSizeByMetricUnit()
90+
=====================
91+
92+
.. versionadded:: 4.6.0
93+
94+
Returns the size of the file default in bytes. You can pass in different FileSizeUnit values as the first parameter to get
95+
the results in kilobytes, megabytes etc. respectively. You can pass in a precision value as the second parameter to define
96+
the amount of decimal places.
97+
98+
.. literalinclude:: files/018.php
99+
:lines: 4-
100+
101+
A ``RuntimeException`` will be thrown if the file does not exist or an error occurs.
102+
67103
getMimeType()
68104
=============
69105

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use CodeIgniter\Files\FileSizeUnit;
4+
5+
$bytes = $file->getSizeByBinaryUnit(); // 256901
6+
$kibibytes = $file->getSizeByBinaryUnit(FileSizeUnit::KB); // 250.880
7+
$mebibytes = $file->getSizeByBinaryUnit(FileSizeUnit::MB); // 0.245
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use CodeIgniter\Files\FileSizeUnit;
4+
5+
$bytes = $file->getSizeByMetricUnit(); // 256901
6+
$kilobytes = $file->getSizeByMetricUnit(FileSizeUnit::KB); // 256.901
7+
$megabytes = $file->getSizeByMetricUnit(FileSizeUnit::MB); // 0.256

0 commit comments

Comments
 (0)