Skip to content

Commit 476ccad

Browse files
authored
Added validation rules contains, extensions and hex_color. (#7313)
1 parent a3d39fc commit 476ccad

File tree

7 files changed

+280
-2
lines changed

7 files changed

+280
-2
lines changed

publish/en/validation.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
],
4242
'boolean' => 'The :attribute field must be true or false.',
4343
'confirmed' => 'The :attribute confirmation does not match.',
44+
'contains' => 'The :attribute is missing a required value.',
4445
'date' => 'The :attribute is not a valid date.',
4546
'date_equals' => 'The :attribute must be a date equal to :date.',
4647
'date_format' => 'The :attribute does not match the format :format.',
@@ -56,7 +57,9 @@
5657
'doesnt_start_with' => 'The :attribute must not start with one of the following: :values.',
5758
'email' => 'The :attribute must be a valid email address.',
5859
'ends_with' => 'The :attribute must end with one of the following: :values.',
60+
'enum' => 'The selected :attribute is invalid.',
5961
'exists' => 'The selected :attribute is invalid.',
62+
'extensions' => 'The :attribute must have one of the following extensions: :values.',
6063
'file' => 'The :attribute must be a file.',
6164
'filled' => 'The :attribute field is required.',
6265
'gt' => [
@@ -71,6 +74,7 @@
7174
'string' => 'The :attribute must be great than or equal to :value characters',
7275
'array' => 'The :attribute must be great than or equal to :value items',
7376
],
77+
'hex_color' => 'The :attribute must be a valid hexadecimal color.',
7478
'image' => 'The :attribute must be an image.',
7579
'in' => 'The selected :attribute is invalid.',
7680
'in_array' => 'The :attribute field does not exist in :other.',
@@ -93,6 +97,7 @@
9397
'string' => 'The :attribute must be less than or equal to :value characters',
9498
'array' => 'The :attribute must be less than or equal to :value items',
9599
],
100+
'mac_address' => 'The :attribute must be a valid MAC address.',
96101
'max' => [
97102
'numeric' => 'The :attribute may not be greater than :max.',
98103
'file' => 'The :attribute may not be greater than :max kilobytes.',

src/Concerns/ReplacesAttributes.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ protected function replaceDigitsBetween(string $message, string $attribute, stri
9494
return $this->replaceBetween($message, $attribute, $rule, $parameters);
9595
}
9696

97+
/**
98+
* Replace all place-holders for the extensions rule.
99+
*/
100+
protected function replaceExtensions(string $message, string $attribute, string $rule, array $parameters): string
101+
{
102+
return str_replace(':values', implode(', ', $parameters), $message);
103+
}
104+
97105
/**
98106
* Replace all place-holders for the min rule.
99107
*/

src/Concerns/ValidatesAttributes.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,27 @@ public function validateConfirmed(string $attribute, $value): bool
322322
return $this->validateSame($attribute, $value, [$attribute . '_confirmation']);
323323
}
324324

325+
/**
326+
* Validate an attribute contains a list of values.
327+
*
328+
* @param mixed $value
329+
* @param array<int, int|string> $parameters
330+
*/
331+
public function validateContains(string $attribute, $value, array $parameters): bool
332+
{
333+
if (! is_array($value)) {
334+
return false;
335+
}
336+
337+
foreach ($parameters as $parameter) {
338+
if (! in_array($parameter, $value)) {
339+
return false;
340+
}
341+
}
342+
343+
return true;
344+
}
345+
325346
/**
326347
* Validate that an attribute is a valid date.
327348
*
@@ -635,6 +656,25 @@ public function guessColumnForQuery(string $attribute): string
635656
return $attribute;
636657
}
637658

659+
/**
660+
* Validate the extension of a file upload attribute is in a set of defined extensions.
661+
*
662+
* @param mixed $value
663+
* @param array<int, int|string> $parameters
664+
*/
665+
public function validateExtensions(string $attribute, $value, array $parameters): bool
666+
{
667+
if (! $this->isValidFileInstance($value)) {
668+
return false;
669+
}
670+
671+
if ($this->shouldBlockPhpUpload($value, $parameters)) {
672+
return false;
673+
}
674+
675+
return in_array(strtolower($value->getExtension()), $parameters);
676+
}
677+
638678
/**
639679
* Validate the given value is a valid file.
640680
*
@@ -791,6 +831,16 @@ public function validateUppercase(string $attribute, mixed $value, array $parame
791831
return Str::upper($value) === $value;
792832
}
793833

834+
/**
835+
* Validate that an attribute is a valid HEX color.
836+
*
837+
* @param mixed $value
838+
*/
839+
public function validateHexColor(string $attribute, $value): bool
840+
{
841+
return preg_match('/^#(?:(?:[0-9a-f]{3}){1,2}|(?:[0-9a-f]{4}){1,2})$/i', $value) === 1;
842+
}
843+
794844
/**
795845
* Validate the MIME type of a file is an image MIME type.
796846
*

src/Rules/File.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ class File implements Rule, DataAwareRule, ValidatorAwareRule
4343
*/
4444
protected array $allowedMimetypes = [];
4545

46+
/**
47+
* The extensions that the given file should match.
48+
*
49+
* @var array
50+
*/
51+
protected $allowedExtensions = [];
52+
4653
/**
4754
* The minimum size in kilobytes that the file can be.
4855
*/
@@ -124,6 +131,19 @@ public static function types(array|string $mimetypes): static
124131
return \Hyperf\Tappable\tap(new static(), fn ($file) => $file->allowedMimetypes = (array) $mimetypes);
125132
}
126133

134+
/**
135+
* Limit the uploaded file to the given file extensions.
136+
*
137+
* @param array<int, string>|string $extensions
138+
* @return $this
139+
*/
140+
public function extensions($extensions): static
141+
{
142+
$this->allowedExtensions = (array) $extensions;
143+
144+
return $this;
145+
}
146+
127147
/**
128148
* Indicate that the uploaded file should be exactly a certain size in kilobytes.
129149
*
@@ -276,6 +296,10 @@ protected function buildValidationRules()
276296

277297
$rules = array_merge($rules, $this->buildMimetypes());
278298

299+
if (! empty($this->allowedExtensions)) {
300+
$rules[] = 'extensions:' . implode(',', array_map('strtolower', $this->allowedExtensions));
301+
}
302+
279303
$rules[] = match (true) {
280304
is_null($this->minimumFileSize) && is_null($this->maximumFileSize) => null,
281305
is_null($this->maximumFileSize) => "min:{$this->minimumFileSize}",

src/Validator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ class Validator implements ValidatorContract
128128
* The validation rules that may be applied to files.
129129
*/
130130
protected array $fileRules = [
131-
'File', 'Image', 'Mimes', 'Mimetypes', 'Min',
132-
'Max', 'Size', 'Between', 'Dimensions',
131+
'Between', 'Dimensions', 'Extensions', 'File', 'Image',
132+
'Max', 'Mimes', 'Mimetypes', 'Min', 'Size',
133133
];
134134

135135
/**

tests/Cases/ValidationFileRuleTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,52 @@ public function testMixOfMimetypesAndMimes()
133133
);
134134
}
135135

136+
public function testSingleExtension()
137+
{
138+
$this->fails(
139+
File::default()->extensions('png'),
140+
(new FileFactory())->createWithContent('foo', file_get_contents(__DIR__ . '/fixtures/image.png')),
141+
['validation.extensions']
142+
);
143+
144+
$this->fails(
145+
File::default()->extensions('png'),
146+
(new FileFactory())->createWithContent('foo.jpg', file_get_contents(__DIR__ . '/fixtures/image.png')),
147+
['validation.extensions']
148+
);
149+
150+
$this->fails(
151+
File::default()->extensions('jpeg'),
152+
(new FileFactory())->createWithContent('foo.jpg', file_get_contents(__DIR__ . '/fixtures/image.png')),
153+
['validation.extensions']
154+
);
155+
156+
$this->passes(
157+
File::default()->extensions('png'),
158+
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
159+
);
160+
}
161+
162+
public function testMultipleExtensions()
163+
{
164+
$this->fails(
165+
File::default()->extensions(['png', 'jpeg', 'jpg']),
166+
(new FileFactory())->createWithContent('foo', file_get_contents(__DIR__ . '/fixtures/image.png')),
167+
['validation.extensions']
168+
);
169+
170+
$this->fails(
171+
File::default()->extensions(['png', 'jpeg']),
172+
(new FileFactory())->createWithContent('foo.jpg', file_get_contents(__DIR__ . '/fixtures/image.png')),
173+
['validation.extensions']
174+
);
175+
176+
$this->passes(
177+
File::default()->extensions(['png', 'jpeg', 'jpg']),
178+
(new FileFactory())->createWithContent('foo.png', file_get_contents(__DIR__ . '/fixtures/image.png')),
179+
);
180+
}
181+
136182
public function testImage()
137183
{
138184
$this->fails(

0 commit comments

Comments
 (0)