Skip to content

Commit 4429142

Browse files
committed
refactor to resolve units from suffix
1 parent 914b354 commit 4429142

File tree

2 files changed

+195
-332
lines changed

2 files changed

+195
-332
lines changed

src/Illuminate/Validation/Rules/File.php

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ class File implements Rule, DataAwareRule, ValidatorAwareRule
2020
/**
2121
* Binary units flag used for size validation.
2222
*/
23-
public const BINARY = 'binary';
23+
protected const BINARY = 'binary';
2424

2525
/**
2626
* International units flag used for size validation.
2727
*/
28-
public const INTERNATIONAL = 'international';
28+
protected const INTERNATIONAL = 'international';
2929

3030
/**
3131
* The MIME types that the given file should match. This array may also contain file extensions.
@@ -42,14 +42,14 @@ class File implements Rule, DataAwareRule, ValidatorAwareRule
4242
protected $allowedExtensions = [];
4343

4444
/**
45-
* The minimum size in kilobytes that the file can be.
45+
* The minimum file size that the file can be.
4646
*
4747
* @var null|int
4848
*/
4949
protected $minimumFileSize = null;
5050

5151
/**
52-
* The maximum size in kilobytes that the file can be.
52+
* The maximum file size that the file can be.
5353
*
5454
* @var null|int
5555
*/
@@ -166,7 +166,9 @@ public function extensions($extensions)
166166
}
167167

168168
/**
169-
* Set the units for size validation to binary.
169+
* Set the units for size validation to binary (1024-based).
170+
*
171+
* Only affects naked numeric values. Explicit suffixes (MB, MiB) take precedence.
170172
*/
171173
public function binary(): static
172174
{
@@ -175,70 +177,84 @@ public function binary(): static
175177
}
176178

177179
/**
178-
* Set the units for size validation to international.
180+
* Set the units for size validation to international (1000-based).
181+
*
182+
* Only affects naked numeric values. Explicit suffixes (MB, MiB) take precedence.
179183
*/
180184
public function international(): static
181185
{
182186
$this->units = self::INTERNATIONAL;
183187
return $this;
184188
}
185189

186-
187-
188190
/**
189-
* Indicate that the uploaded file should be exactly a certain size in kilobytes.
191+
* Indicate that the uploaded file should be exactly a certain size.
192+
*
193+
* Supports both numeric values and human-readable sizes with suffixes:
194+
* - Binary: 1KiB, 1MiB, 1GiB, 1TiB (1024-based)
195+
* - International: 1KB, 1MB, 1GB, 1TB (1000-based)
196+
* - Numeric: Uses instance unit setting (binary/international)
190197
*/
191-
public function size(string|int $size, ?string $units = null): static
198+
public function size(string|int $size): static
192199
{
193-
$this->minimumFileSize = $this->toKilobytes($size, $this->units($units));
200+
$this->minimumFileSize = $this->toKilobytes($size);
194201
$this->maximumFileSize = $this->minimumFileSize;
195202

196203
return $this;
197204
}
198205

199206
/**
200-
* Indicate that the uploaded file should be between a minimum and maximum size in kilobytes.
207+
* Indicate that the uploaded file should be between a minimum and maximum size.
208+
*
209+
* Supports both numeric values and human-readable sizes with suffixes:
210+
* - Binary: 1KiB, 1MiB, 1GiB, 1TiB (1024-based)
211+
* - International: 1KB, 1MB, 1GB, 1TB (1000-based)
212+
* - Numeric: Uses instance unit setting (binary/international)
201213
*/
202-
public function between(string|int $minSize, string|int $maxSize, ?string $units = null): static
214+
public function between(string|int $minSize, string|int $maxSize): static
203215
{
204-
$this->minimumFileSize = $this->toKilobytes($minSize, $this->units($units));
205-
$this->maximumFileSize = $this->toKilobytes($maxSize, $this->units($units));
216+
$this->minimumFileSize = $this->toKilobytes($minSize);
217+
$this->maximumFileSize = $this->toKilobytes($maxSize);
206218

207219
return $this;
208220
}
209221

210222
/**
211-
* Indicate that the uploaded file should be no less than the given number of kilobytes.
223+
* Indicate that the uploaded file should be no less than the given size.
224+
*
225+
* Supports both numeric values and human-readable sizes with suffixes:
226+
* - Binary: 1KiB, 1MiB, 1GiB, 1TiB (1024-based)
227+
* - International: 1KB, 1MB, 1GB, 1TB (1000-based)
228+
* - Numeric: Uses instance unit setting (binary/international)
212229
*/
213-
public function min(string|int $size, ?string $units = null): static
230+
public function min(string|int $size): static
214231
{
215-
$this->minimumFileSize = $this->toKilobytes($size, $this->units($units));
232+
$this->minimumFileSize = $this->toKilobytes($size);
216233

217234
return $this;
218235
}
219236

220237
/**
221-
* Indicate that the uploaded file should be no more than the given number of kilobytes.
238+
* Indicate that the uploaded file should be no more than the given size.
239+
*
240+
* Supports both numeric values and human-readable sizes with suffixes:
241+
* - Binary: 1KiB, 1MiB, 1GiB, 1TiB (1024-based)
242+
* - International: 1KB, 1MB, 1GB, 1TB (1000-based)
243+
* - Numeric: Uses instance unit setting (binary/international)
222244
*/
223-
public function max(string|int $size, ?string $units = null): static
245+
public function max(string|int $size): static
224246
{
225-
$this->maximumFileSize = $this->toKilobytes($size, $this->units($units));
247+
$this->maximumFileSize = $this->toKilobytes($size);
226248

227249
return $this;
228250
}
229251

230-
/**
231-
* Resolve the units to use for size calculations.
232-
*/
233-
protected function units(?string $units = null): string
234-
{
235-
return $units ?? $this->units;
236-
}
237-
238252
/**
239253
* Convert a potentially human-friendly file size to kilobytes.
254+
*
255+
* Supports suffix detection with precedence over instance settings.
240256
*/
241-
protected function toKilobytes(string|int $size, string $units): float|int
257+
protected function toKilobytes(string|int $size): float|int
242258
{
243259
if (! is_string($size)) {
244260
return $size;
@@ -247,8 +263,8 @@ protected function toKilobytes(string|int $size, string $units): float|int
247263
if (($value = $this->parseSize($size)) === false || $value < 0) {
248264
throw new InvalidArgumentException('Invalid numeric value in file size.');
249265
}
250-
251-
return $units === self::BINARY
266+
267+
return $this->detectUnits($size) === self::BINARY
252268
? $this->toBinaryKilobytes($size, $value)
253269
: $this->toInternationalKilobytes($size, $value);
254270
}
@@ -266,6 +282,22 @@ protected function parseSize($size): false|float
266282
);
267283
}
268284

285+
/**
286+
* Detect the suffix and determine appropriate units from a file size string.
287+
*
288+
* Returns binary for MiB/GiB/TiB suffixes, international for MB/GB/TB suffixes,
289+
* or falls back to instance setting for naked values.
290+
*/
291+
protected function detectUnits(string $size): string
292+
{
293+
return match (true) {
294+
is_numeric($size) => $this->units,
295+
in_array(strtolower(substr(trim($size), -3)), ['kib', 'mib', 'gib', 'tib']) => self::BINARY,
296+
in_array(strtolower(substr(trim($size), -2)), ['kb', 'mb', 'gb', 'tb']) => self::INTERNATIONAL,
297+
default => $this->units,
298+
};
299+
}
300+
269301
/**
270302
* Convert a human-friendly file size to kilobytes using the International System.
271303
*/
@@ -275,19 +307,26 @@ protected function toInternationalKilobytes(string $size, float $value): float|i
275307
$this->protectValueFromOverflow(
276308
$this->prepareValueForPrecision($value),
277309
! is_numeric($size)
278-
? match (substr(strtolower(trim($size)), -2)) {
279-
'kb' => 1,
280-
'mb' => 1_000,
281-
'gb' => 1_000_000,
282-
'tb' => 1_000_000_000,
283-
default => throw new InvalidArgumentException(
284-
'Invalid file size suffix. Valid suffixes are: KB, MB, GB, TB (case insensitive).'
285-
),
286-
} : 1
310+
? $this->getInternationalMultiplier(strtolower(trim($size)))
311+
: 1
287312
)
288313
);
289314
}
290315

316+
/**
317+
* Get the international multiplier for a given size string.
318+
*/
319+
protected function getInternationalMultiplier(string $size): int
320+
{
321+
return match (substr($size, -2)) {
322+
'kb' => 1,
323+
'mb' => 1_000,
324+
'gb' => 1_000_000,
325+
'tb' => 1_000_000_000,
326+
default => throw new InvalidArgumentException('Invalid file size suffix.'),
327+
};
328+
}
329+
291330
/**
292331
* Convert a human-friendly file size to kilobytes using the Binary System.
293332
*/
@@ -297,19 +336,26 @@ protected function toBinaryKilobytes(string $size, float $value): float|int
297336
$this->protectValueFromOverflow(
298337
$this->prepareValueForPrecision($value),
299338
! is_numeric($size)
300-
? match (substr(strtolower(trim($size)), -2)) {
301-
'kb' => 1,
302-
'mb' => 1_024,
303-
'gb' => 1_048_576,
304-
'tb' => 1_073_741_824,
305-
default => throw new InvalidArgumentException(
306-
'Invalid file size suffix. Valid suffixes are: KB, MB, GB, TB (case insensitive).'
307-
),
308-
} : 1
339+
? $this->getBinaryMultiplier(strtolower(trim($size)))
340+
: 1
309341
)
310342
);
311343
}
312344

345+
/**
346+
* Get the binary multiplier for a given size string.
347+
*/
348+
protected function getBinaryMultiplier(string $size): int
349+
{
350+
return match (substr($size, -3)) {
351+
'kib' => 1,
352+
'mib' => 1_024,
353+
'gib' => 1_048_576,
354+
'tib' => 1_073_741_824,
355+
default => throw new InvalidArgumentException('Invalid file size suffix.'),
356+
};
357+
}
358+
313359
/**
314360
* Converts whole numbers to integers for exact arithmetic while keeping
315361
* fractional numbers as floats; also provides overflow protection by

0 commit comments

Comments
 (0)