Skip to content

Commit a032838

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

File tree

2 files changed

+256
-424
lines changed

2 files changed

+256
-424
lines changed

src/Illuminate/Validation/Rules/File.php

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@
1313
use Illuminate\Support\Traits\Macroable;
1414
use InvalidArgumentException;
1515

16-
class File implements Rule, DataAwareRule, ValidatorAwareRule
16+
class File implements DataAwareRule, Rule, ValidatorAwareRule
1717
{
1818
use Conditionable, Macroable;
1919

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
*/
@@ -127,7 +127,7 @@ public static function default()
127127
? call_user_func(static::$defaultCallback)
128128
: static::$defaultCallback;
129129

130-
return $file instanceof Rule ? $file : new self();
130+
return $file instanceof Rule ? $file : new self;
131131
}
132132

133133
/**
@@ -149,7 +149,7 @@ public static function image($allowSvg = false)
149149
*/
150150
public static function types($mimetypes)
151151
{
152-
return tap(new static(), fn ($file) => $file->allowedMimetypes = (array) $mimetypes);
152+
return tap(new static, fn ($file) => $file->allowedMimetypes = (array) $mimetypes);
153153
}
154154

155155
/**
@@ -166,89 +166,83 @@ 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).
170170
*/
171171
public function binary(): static
172172
{
173173
$this->units = self::BINARY;
174+
174175
return $this;
175176
}
176177

177178
/**
178-
* Set the units for size validation to international.
179+
* Set the units for size validation to international (1000-based).
179180
*/
180181
public function international(): static
181182
{
182183
$this->units = self::INTERNATIONAL;
184+
183185
return $this;
184186
}
185187

186-
187-
188188
/**
189-
* Indicate that the uploaded file should be exactly a certain size in kilobytes.
189+
* Indicate that the uploaded file should be exactly a certain size.
190190
*/
191-
public function size(string|int $size, ?string $units = null): static
191+
public function size(string|int $size): static
192192
{
193-
$this->minimumFileSize = $this->toKilobytes($size, $this->units($units));
193+
$this->minimumFileSize = $this->toKilobytes($size);
194194
$this->maximumFileSize = $this->minimumFileSize;
195195

196196
return $this;
197197
}
198198

199199
/**
200-
* Indicate that the uploaded file should be between a minimum and maximum size in kilobytes.
200+
* Indicate that the uploaded file should be between a minimum and maximum size.
201201
*/
202-
public function between(string|int $minSize, string|int $maxSize, ?string $units = null): static
202+
public function between(string|int $minSize, string|int $maxSize): static
203203
{
204-
$this->minimumFileSize = $this->toKilobytes($minSize, $this->units($units));
205-
$this->maximumFileSize = $this->toKilobytes($maxSize, $this->units($units));
204+
$this->minimumFileSize = $this->toKilobytes($minSize);
205+
$this->maximumFileSize = $this->toKilobytes($maxSize);
206206

207207
return $this;
208208
}
209209

210210
/**
211-
* Indicate that the uploaded file should be no less than the given number of kilobytes.
211+
* Indicate that the uploaded file should be no less than the given size.
212212
*/
213-
public function min(string|int $size, ?string $units = null): static
213+
public function min(string|int $size): static
214214
{
215-
$this->minimumFileSize = $this->toKilobytes($size, $this->units($units));
215+
$this->minimumFileSize = $this->toKilobytes($size);
216216

217217
return $this;
218218
}
219219

220220
/**
221-
* Indicate that the uploaded file should be no more than the given number of kilobytes.
221+
* Indicate that the uploaded file should be no more than the given size.
222222
*/
223-
public function max(string|int $size, ?string $units = null): static
223+
public function max(string|int $size): static
224224
{
225-
$this->maximumFileSize = $this->toKilobytes($size, $this->units($units));
225+
$this->maximumFileSize = $this->toKilobytes($size);
226226

227227
return $this;
228228
}
229229

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-
238230
/**
239231
* Convert a potentially human-friendly file size to kilobytes.
232+
*
233+
* Supports suffix detection with precedence over instance settings.
240234
*/
241-
protected function toKilobytes(string|int $size, string $units): float|int
235+
protected function toKilobytes(string|int $size): float|int
242236
{
243237
if (! is_string($size)) {
244238
return $size;
245239
}
246240

247241
if (($value = $this->parseSize($size)) === false || $value < 0) {
248242
throw new InvalidArgumentException('Invalid numeric value in file size.');
249-
}
243+
}
250244

251-
return $units === self::BINARY
245+
return $this->detectUnits($size) === self::BINARY
252246
? $this->toBinaryKilobytes($size, $value)
253247
: $this->toInternationalKilobytes($size, $value);
254248
}
@@ -266,6 +260,19 @@ protected function parseSize($size): false|float
266260
);
267261
}
268262

263+
/**
264+
* Detect the suffix and determine appropriate units from a file size string.
265+
*/
266+
protected function detectUnits(string $size): string
267+
{
268+
return match (true) {
269+
is_numeric($size) => $this->units,
270+
in_array(strtolower(substr(trim($size), -3)), ['kib', 'mib', 'gib', 'tib']) => self::BINARY,
271+
in_array(strtolower(substr(trim($size), -2)), ['kb', 'mb', 'gb', 'tb']) => self::INTERNATIONAL,
272+
default => $this->units,
273+
};
274+
}
275+
269276
/**
270277
* Convert a human-friendly file size to kilobytes using the International System.
271278
*/
@@ -275,17 +282,24 @@ protected function toInternationalKilobytes(string $size, float $value): float|i
275282
$this->protectValueFromOverflow(
276283
$this->prepareValueForPrecision($value),
277284
! 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
287-
)
288-
);
285+
? $this->getInternationalMultiplier(strtolower(trim($size)))
286+
: 1
287+
)
288+
);
289+
}
290+
291+
/**
292+
* Get the international multiplier for a given size string.
293+
*/
294+
protected function getInternationalMultiplier(string $size): int
295+
{
296+
return match (substr($size, -2)) {
297+
'kb' => 1,
298+
'mb' => 1_000,
299+
'gb' => 1_000_000,
300+
'tb' => 1_000_000_000,
301+
default => throw new InvalidArgumentException('Invalid file size suffix.'),
302+
};
289303
}
290304

291305
/**
@@ -297,17 +311,24 @@ protected function toBinaryKilobytes(string $size, float $value): float|int
297311
$this->protectValueFromOverflow(
298312
$this->prepareValueForPrecision($value),
299313
! 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
309-
)
310-
);
314+
? $this->getBinaryMultiplier(strtolower(trim($size)))
315+
: 1
316+
)
317+
);
318+
}
319+
320+
/**
321+
* Get the binary multiplier for a given size string.
322+
*/
323+
protected function getBinaryMultiplier(string $size): int
324+
{
325+
return match (substr($size, -3)) {
326+
'kib' => 1,
327+
'mib' => 1_024,
328+
'gib' => 1_048_576,
329+
'tib' => 1_073_741_824,
330+
default => throw new InvalidArgumentException('Invalid file size suffix.'),
331+
};
311332
}
312333

313334
/**
@@ -329,7 +350,7 @@ protected function prepareValueForPrecision(float $value): float|int
329350
*/
330351
protected function protectValueFromOverflow(float|int $value, int $multiplier): float|int
331352
{
332-
return $value > PHP_INT_MAX / $multiplier
353+
return $value > PHP_INT_MAX / $multiplier
333354
|| $value < PHP_INT_MIN / $multiplier
334355
|| is_float($value)
335356
? (float) $value * $multiplier

0 commit comments

Comments
 (0)