Skip to content
Merged
1 change: 1 addition & 0 deletions src/Illuminate/Translation/lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.',
'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.',
'email' => 'The :attribute field must be a valid email address.',
'encoding' => 'The :attribute field must encoded in :encoding.',
'ends_with' => 'The :attribute field must end with one of the following: :values.',
'enum' => 'The selected :attribute is invalid.',
'exists' => 'The selected :attribute is invalid.',
Expand Down
14 changes: 14 additions & 0 deletions src/Illuminate/Validation/Concerns/ReplacesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,20 @@ protected function replaceDigitsBetween($message, $attribute, $rule, $parameters
return $this->replaceBetween($message, $attribute, $rule, $parameters);
}

/**
* Replace all place-holders for the encoding rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceEncoding($message, $attribute, $rule, $parameters)
{
return str_replace(':encoding', $parameters[0], $message);
}

/**
* Replace all place-holders for the extensions rule.
*
Expand Down
19 changes: 19 additions & 0 deletions src/Illuminate/Validation/Concerns/ValidatesAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,25 @@ public function validateEmail($attribute, $value, $parameters)
return $emailValidator->isValid($value, new MultipleValidationWithAnd($validations));
}

/**
* Validate the encoding of an attribute.
*
* @param string $attribute
* @param mixed $value
* @param array<int, int|string> $parameters
* @return bool
*/
public function validateEncoding($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'encoding');

if (! in_array(mb_strtolower($parameters[0]), array_map(mb_strtolower(...), mb_list_encodings()))) {
throw new InvalidArgumentException("Validation rule encoding parameter [{$parameters[0]}] is not a valid encoding.");
}

return mb_check_encoding($value instanceof File ? $value->getContent() : $value, $parameters[0]);
}

/**
* Validate the existence of an attribute value in a database table.
*
Expand Down
24 changes: 24 additions & 0 deletions src/Illuminate/Validation/Rules/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ class File implements Rule, DataAwareRule, ValidatorAwareRule
*/
protected $maximumFileSize = null;

/**
* The required file encoding.
*
* @var string|null
*/
protected $encoding = null;

/**
* An array of custom rules that will be merged into the validation rules.
*
Expand Down Expand Up @@ -205,6 +212,19 @@ public function max($size)
return $this;
}

/**
* Indicate that the uploaded file should be in the given encoding.
*
* @param string $encoding
* @return $this
*/
public function encoding($encoding)
{
$this->encoding = $encoding;

return $this;
}

/**
* Convert a potentially human-friendly file size to kilobytes.
*
Expand Down Expand Up @@ -291,6 +311,10 @@ protected function buildValidationRules()
default => "size:{$this->minimumFileSize}",
};

if ($this->encoding) {
$rules[] = 'encoding:'.$this->encoding;
}

return array_merge(array_filter($rules), $this->customRules);
}

Expand Down
1 change: 1 addition & 0 deletions src/Illuminate/Validation/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class Validator implements ValidatorContract
protected $fileRules = [
'Between',
'Dimensions',
'Encoding',
'Extensions',
'File',
'Image',
Expand Down
43 changes: 43 additions & 0 deletions tests/Validation/ValidationFileRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,49 @@ public function testMaxWithHumanReadableSizeAndMultipleValue()
);
}

public function testEncoding()
{
// ASCII file containing UTF-8.
$this->fails(
File::default()->encoding('ascii'),
UploadedFile::fake()->createWithContent('foo.txt', '✌️'),
['validation.encoding'],
);

// UTF-8 file containing invalid UTF-8 byte sequence.
$this->fails(
File::default()->encoding('utf-8'),
UploadedFile::fake()->createWithContent('foo.txt', "\xf0\x28\x8c\x28"),
['validation.encoding'],
);

$this->passes(
File::default()->encoding('utf-8'),
UploadedFile::fake()->createWithContent('foo.txt', '✌️'),
);

$this->passes(
File::default()->encoding('utf-8'),
[
UploadedFile::fake()->createWithContent('foo-1.txt', '✌️'),
UploadedFile::fake()->createWithContent('foo-2.txt', '👍'),
]
);
}

public function testEncodingWithInvalidParameter()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Validation rule encoding parameter "FOOBAR" is not a valid encoding.');

// Invalid encoding.
$this->fails(
File::default()->encoding('FOOBAR'),
UploadedFile::fake()->createWithContent('foo.txt', ''),
['validation.encoding'],
);
}

public function testMacro()
{
File::macro('toDocument', function () {
Expand Down
Loading