minor #10951 Made use of "kB" vs. "KiB" consistent (Yannick, webmozart)

This PR was merged into the 2.5-dev branch.

Discussion
----------

Made use of "kB" vs. "KiB" consistent

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Continuation of #10661.

This PR makes the usage of "kB" and "KiB" consistent across the project. The notations equal the internationally recognized ones:

Short form | Long form | Value in Bytes
--- | --- | ---
B | bytes | 1
kB | kilobytes | 1000
KiB | kibibytes | 1024
MB | megabytes | 1000000
MiB | mebibytes | 1048576
GB | gigabytes | 1000000000
GiB | gibibytes | 1073741824

The reason for differentiating between the two is that several users got confused with the current mix (see #10648, #10917, #10661).

FileValidator, UploadedFile and the ProgressBar helper were changed accordingly.

Follow-up feature request: #10962

Commits
-------

e4c6da5 [Validator] Improved to-string conversion of the file size/size limit
bbe1045 [Validator][Console][HttpFoundation] Use "KiB" everywhere (instead of "kB")
This commit is contained in:
Fabien Potencier 2014-05-22 15:36:06 +02:00
commit 3c9d8837cf
5 changed files with 134 additions and 63 deletions

View File

@ -92,15 +92,15 @@ abstract class Helper implements HelperInterface
public static function formatMemory($memory)
{
if ($memory >= 1024 * 1024 * 1024) {
return sprintf('%.1f GB', $memory / 1024 / 1024 / 1024);
return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
}
if ($memory >= 1024 * 1024) {
return sprintf('%.1f MB', $memory / 1024 / 1024);
return sprintf('%.1f MiB', $memory / 1024 / 1024);
}
if ($memory >= 1024) {
return sprintf('%d kB', $memory / 1024);
return sprintf('%d KiB', $memory / 1024);
}
return sprintf('%d B', $memory);

View File

@ -367,17 +367,17 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
$this->generateOutput(
" \033[44;37m Starting the demo... fingers crossed \033[0m\n".
" 0/15 ".$progress.str_repeat($empty, 26)." 0%\n".
" 🏁 1 sec \033[44;37m 0 B \033[0m"
" \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m"
).
$this->generateOutput(
" \033[44;37m Looks good to me... \033[0m\n".
" 4/15 ".str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
" 🏁 1 sec \033[41;37m 97 kB \033[0m"
" \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m"
).
$this->generateOutput(
" \033[44;37m Thanks, bye \033[0m\n".
" 15/15 ".str_repeat($done, 28)." 100%\n".
" 🏁 1 sec \033[41;37m 195 kB \033[0m"
" \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m"
),
stream_get_contents($output->getStream())
);

View File

@ -291,7 +291,7 @@ class UploadedFile extends File
public function getErrorMessage()
{
static $errors = array(
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d kb).',
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',

View File

@ -25,6 +25,16 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
*/
class FileValidator extends ConstraintValidator
{
const KB_BYTES = 1000;
const MB_BYTES = 1000000;
private static $suffices = array(
1 => 'bytes',
self::KB_BYTES => 'kB',
self::MB_BYTES => 'MB',
);
/**
* {@inheritdoc}
*/
@ -43,21 +53,21 @@ class FileValidator extends ConstraintValidator
case UPLOAD_ERR_INI_SIZE:
if ($constraint->maxSize) {
if (ctype_digit((string) $constraint->maxSize)) {
$maxSize = (int) $constraint->maxSize;
$limitInBytes = (int) $constraint->maxSize;
} elseif (preg_match('/^\d++k$/', $constraint->maxSize)) {
$maxSize = $constraint->maxSize * 1024;
$limitInBytes = $constraint->maxSize * self::KB_BYTES;
} elseif (preg_match('/^\d++M$/', $constraint->maxSize)) {
$maxSize = $constraint->maxSize * 1048576;
$limitInBytes = $constraint->maxSize * self::MB_BYTES;
} else {
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));
}
$maxSize = min(UploadedFile::getMaxFilesize(), $maxSize);
$limitInBytes = min(UploadedFile::getMaxFilesize(), $limitInBytes);
} else {
$maxSize = UploadedFile::getMaxFilesize();
$limitInBytes = UploadedFile::getMaxFilesize();
}
$this->context->addViolation($constraint->uploadIniSizeErrorMessage, array(
'{{ limit }}' => $maxSize,
'{{ limit }}' => $limitInBytes,
'{{ suffix }}' => 'bytes',
));
@ -112,27 +122,45 @@ class FileValidator extends ConstraintValidator
}
if ($constraint->maxSize) {
if (ctype_digit((string) $constraint->maxSize)) {
$size = filesize($path);
$limit = (int) $constraint->maxSize;
$suffix = 'bytes';
} elseif (preg_match('/^\d++k$/', $constraint->maxSize)) {
$size = round(filesize($path) / 1000, 2);
$limit = (int) $constraint->maxSize;
$suffix = 'kB';
$sizeInBytes = filesize($path);
$limitInBytes = (int) $constraint->maxSize;
if (preg_match('/^\d++k$/', $constraint->maxSize)) {
$limitInBytes *= self::KB_BYTES;
} elseif (preg_match('/^\d++M$/', $constraint->maxSize)) {
$size = round(filesize($path) / 1000000, 2);
$limit = (int) $constraint->maxSize;
$suffix = 'MB';
} else {
$limitInBytes *= self::MB_BYTES;
} elseif (!ctype_digit((string) $constraint->maxSize)) {
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $constraint->maxSize));
}
if ($size > $limit) {
if ($sizeInBytes > $limitInBytes) {
// Convert the limit to the smallest possible number
// (i.e. try "MB", then "kB", then "bytes")
$coef = self::MB_BYTES;
$limitAsString = (string) ($limitInBytes / $coef);
// Restrict the limit to 2 decimals (without rounding! we
// need the precise value)
while (self::moreDecimalsThan($limitAsString, 2)) {
$coef /= 1000;
$limitAsString = (string) ($limitInBytes / $coef);
}
// Convert size to the same measure, but round to 2 decimals
$sizeAsString = (string) round($sizeInBytes / $coef, 2);
// If the size and limit produce the same string output
// (due to rounding), reduce the coefficient
while ($sizeAsString === $limitAsString) {
$coef /= 1000;
$limitAsString = (string) ($limitInBytes / $coef);
$sizeAsString = (string) round($sizeInBytes / $coef, 2);
}
$this->context->addViolation($constraint->maxSizeMessage, array(
'{{ size }}' => $size,
'{{ limit }}' => $limit,
'{{ suffix }}' => $suffix,
'{{ size }}' => $sizeAsString,
'{{ limit }}' => $limitAsString,
'{{ suffix }}' => static::$suffices[$coef],
'{{ file }}' => $path,
));
@ -172,4 +200,9 @@ class FileValidator extends ConstraintValidator
}
}
}
private static function moreDecimalsThan($double, $numberOfDecimals)
{
return strlen((string) $double) > strlen(round($double, $numberOfDecimals));
}
}

View File

@ -33,7 +33,13 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase
protected function tearDown()
{
fclose($this->file);
if (is_resource($this->file)) {
fclose($this->file);
}
if (file_exists($this->path)) {
unlink($this->path);
}
$this->context = null;
$this->validator = null;
@ -82,65 +88,97 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase
$this->validator->validate($file, new File());
}
public function testTooLargeBytes()
public function provideMaxSizeExceededTests()
{
fwrite($this->file, str_repeat('0', 11));
return array(
array(11, 10, '11', '10', 'bytes'),
array(ceil(1.005*1000), ceil(1.005*1000) - 1, '1005', '1004', 'bytes'),
array(ceil(1.005*1000*1000), ceil(1.005*1000*1000) - 1, '1005000', '1004999', 'bytes'),
// round(size) == 1.01kB, limit == 1kB
array(ceil(1.005*1000), 1000, '1.01', '1', 'kB'),
array(ceil(1.005*1000), '1k', '1.01', '1', 'kB'),
// round(size) == 1kB, limit == 1kB -> use bytes
array(ceil(1.004*1000), 1000, '1004', '1000', 'bytes'),
array(ceil(1.004*1000), '1k', '1004', '1000', 'bytes'),
array(1000 + 1, 1000, '1001', '1000', 'bytes'),
array(1000 + 1, '1k', '1001', '1000', 'bytes'),
// round(size) == 1.01MB, limit == 1MB
array(ceil(1.005*1000*1000), 1000*1000, '1.01', '1', 'MB'),
array(ceil(1.005*1000*1000), '1000k', '1.01', '1', 'MB'),
array(ceil(1.005*1000*1000), '1M', '1.01', '1', 'MB'),
// round(size) == 1MB, limit == 1MB -> use kB
array(ceil(1.004*1000*1000), 1000*1000, '1004', '1000', 'kB'),
array(ceil(1.004*1000*1000), '1000k', '1004', '1000', 'kB'),
array(ceil(1.004*1000*1000), '1M', '1004', '1000', 'kB'),
array(1000*1000 + 1, 1000*1000, '1000001', '1000000', 'bytes'),
array(1000*1000 + 1, '1000k', '1000001', '1000000', 'bytes'),
array(1000*1000 + 1, '1M', '1000001', '1000000', 'bytes'),
);
}
/**
* @dataProvider provideMaxSizeExceededTests
*/
public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
{
fseek($this->file, $bytesWritten-1, SEEK_SET);
fwrite($this->file, '0');
fclose($this->file);
$constraint = new File(array(
'maxSize' => 10,
'maxSize' => $limit,
'maxSizeMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ limit }}' => '10',
'{{ size }}' => '11',
'{{ suffix }}' => 'bytes',
'{{ limit }}' => $limitAsString,
'{{ size }}' => $sizeAsString,
'{{ suffix }}' => $suffix,
'{{ file }}' => $this->path,
));
$this->validator->validate($this->getFile($this->path), $constraint);
}
public function testTooLargeKiloBytes()
public function provideMaxSizeNotExceededTests()
{
fwrite($this->file, str_repeat('0', 1400));
return array(
array(10, 10),
array(9, 10),
$constraint = new File(array(
'maxSize' => '1k',
'maxSizeMessage' => 'myMessage',
));
array(1000, '1k'),
array(1000 - 1, '1k'),
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ limit }}' => '1',
'{{ size }}' => '1.4',
'{{ suffix }}' => 'kB',
'{{ file }}' => $this->path,
));
$this->validator->validate($this->getFile($this->path), $constraint);
array(1000*1000, '1M'),
array(1000*1000 - 1, '1M'),
);
}
public function testTooLargeMegaBytes()
/**
* @dataProvider provideMaxSizeNotExceededTests
*/
public function testMaxSizeNotExceeded($bytesWritten, $limit)
{
fwrite($this->file, str_repeat('0', 1400000));
fseek($this->file, $bytesWritten-1, SEEK_SET);
fwrite($this->file, '0');
fclose($this->file);
$constraint = new File(array(
'maxSize' => '1M',
'maxSize' => $limit,
'maxSizeMessage' => 'myMessage',
));
$this->context->expects($this->once())
->method('addViolation')
->with('myMessage', array(
'{{ limit }}' => '1',
'{{ size }}' => '1.4',
'{{ suffix }}' => 'MB',
'{{ file }}' => $this->path,
));
$this->context->expects($this->never())
->method('addViolation');
$this->validator->validate($this->getFile($this->path), $constraint);
}