Skip to content

Commit 55ba068

Browse files
feature #49417 [Validator] Add the option filenameMaxLength to the File constraint (Kevin Auvinet)
This PR was merged into the 6.3 branch. Discussion ---------- [Validator] Add the option filenameMaxLength to the File constraint | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #49359 | License | MIT | Doc PR | TODO symfony/symfony-docs#... <!-- required for new features --> This option allow to define a max length for the uploaded file: ```php // src/Entity/Author.php namespace App\Entity; use Symfony\Component\Validator\Constraints as Assert; class Author { #[Assert\File( maxSize: '1024k', filenameMaxLength: 50, filenameMaxLengthMessage: 'The filename is too long. It should have 50 character or less.' )] protected $bioFile; } ``` Commits ------- 107a6a3 [Validator] Add the option filenameMaxLength to the File constraint
2 parents fa37492 + 107a6a3 commit 55ba068

File tree

5 files changed

+68
-4
lines changed

5 files changed

+68
-4
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Add the `pattern` parameter in violations of the `Regex` constraint
1010
* Add a `NoSuspiciousCharacters` constraint to validate a string is not a spoofing attempt
1111
* Add the `countUnit` option to the `Length` constraint to allow counting the string length either by code points (like before, now the default setting `Length::COUNT_CODEPOINTS`), bytes (`Length::COUNT_BYTES`) or graphemes (`Length::COUNT_GRAPHEMES`)
12+
* Add the `filenameMaxLength` option to the `File` constraint
1213

1314
6.2
1415
---

src/Symfony/Component/Validator/Constraints/File.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ class File extends Constraint
3333
public const TOO_LARGE_ERROR = 'df8637af-d466-48c6-a59d-e7126250a654';
3434
public const INVALID_MIME_TYPE_ERROR = '744f00bc-4389-4c74-92de-9a43cde55534';
3535
public const INVALID_EXTENSION_ERROR = 'c8c7315c-6186-4719-8b71-5659e16bdcb7';
36+
public const FILENAME_TOO_LONG = 'e5706483-91a8-49d8-9a59-5e81a3c634a8';
3637

3738
protected const ERROR_NAMES = [
3839
self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR',
3940
self::NOT_READABLE_ERROR => 'NOT_READABLE_ERROR',
4041
self::EMPTY_ERROR => 'EMPTY_ERROR',
4142
self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR',
4243
self::INVALID_MIME_TYPE_ERROR => 'INVALID_MIME_TYPE_ERROR',
44+
self::FILENAME_TOO_LONG => 'FILENAME_TOO_LONG',
4345
];
4446

4547
/**
@@ -49,13 +51,15 @@ class File extends Constraint
4951

5052
public $binaryFormat;
5153
public $mimeTypes = [];
54+
public ?int $filenameMaxLength = null;
5255
public array|string|null $extensions = [];
5356
public $notFoundMessage = 'The file could not be found.';
5457
public $notReadableMessage = 'The file is not readable.';
5558
public $maxSizeMessage = 'The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.';
5659
public $mimeTypesMessage = 'The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.';
5760
public string $extensionsMessage = 'The extension of the file is invalid ({{ extension }}). Allowed extensions are {{ extensions }}.';
5861
public $disallowEmptyMessage = 'An empty file is not allowed.';
62+
public $filenameTooLongMessage = 'The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less.';
5963

6064
public $uploadIniSizeErrorMessage = 'The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.';
6165
public $uploadFormSizeErrorMessage = 'The file is too large.';
@@ -76,11 +80,13 @@ public function __construct(
7680
int|string $maxSize = null,
7781
bool $binaryFormat = null,
7882
array|string $mimeTypes = null,
83+
int $filenameMaxLength = null,
7984
string $notFoundMessage = null,
8085
string $notReadableMessage = null,
8186
string $maxSizeMessage = null,
8287
string $mimeTypesMessage = null,
8388
string $disallowEmptyMessage = null,
89+
string $filenameTooLongMessage = null,
8490

8591
string $uploadIniSizeErrorMessage = null,
8692
string $uploadFormSizeErrorMessage = null,
@@ -101,13 +107,15 @@ public function __construct(
101107
$this->maxSize = $maxSize ?? $this->maxSize;
102108
$this->binaryFormat = $binaryFormat ?? $this->binaryFormat;
103109
$this->mimeTypes = $mimeTypes ?? $this->mimeTypes;
110+
$this->filenameMaxLength = $filenameMaxLength ?? $this->filenameMaxLength;
104111
$this->extensions = $extensions ?? $this->extensions;
105112
$this->notFoundMessage = $notFoundMessage ?? $this->notFoundMessage;
106113
$this->notReadableMessage = $notReadableMessage ?? $this->notReadableMessage;
107114
$this->maxSizeMessage = $maxSizeMessage ?? $this->maxSizeMessage;
108115
$this->mimeTypesMessage = $mimeTypesMessage ?? $this->mimeTypesMessage;
109116
$this->extensionsMessage = $extensionsMessage ?? $this->extensionsMessage;
110117
$this->disallowEmptyMessage = $disallowEmptyMessage ?? $this->disallowEmptyMessage;
118+
$this->filenameTooLongMessage = $filenameTooLongMessage ?? $this->filenameTooLongMessage;
111119
$this->uploadIniSizeErrorMessage = $uploadIniSizeErrorMessage ?? $this->uploadIniSizeErrorMessage;
112120
$this->uploadFormSizeErrorMessage = $uploadFormSizeErrorMessage ?? $this->uploadFormSizeErrorMessage;
113121
$this->uploadPartialErrorMessage = $uploadPartialErrorMessage ?? $this->uploadPartialErrorMessage;

src/Symfony/Component/Validator/Constraints/FileValidator.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ public function validate(mixed $value, Constraint $constraint)
143143
$sizeInBytes = filesize($path);
144144
$basename = $value instanceof UploadedFile ? $value->getClientOriginalName() : basename($path);
145145

146+
if ($constraint->filenameMaxLength && $constraint->filenameMaxLength < $filenameLength = \strlen($basename)) {
147+
$this->context->buildViolation($constraint->filenameTooLongMessage)
148+
->setParameter('{{ filename_max_length }}', $this->formatValue($constraint->filenameMaxLength))
149+
->setCode(File::FILENAME_TOO_LONG)
150+
->setPlural($constraint->filenameMaxLength)
151+
->addViolation();
152+
153+
return;
154+
}
155+
146156
if (0 === $sizeInBytes) {
147157
$this->context->buildViolation($constraint->disallowEmptyMessage)
148158
->setParameter('{{ file }}', $this->formatValue($path))

src/Symfony/Component/Validator/Constraints/Image.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public function __construct(
9898
int|string $maxSize = null,
9999
bool $binaryFormat = null,
100100
array $mimeTypes = null,
101+
int $filenameMaxLength = null,
101102
int $minWidth = null,
102103
int $maxWidth = null,
103104
int $maxHeight = null,
@@ -115,6 +116,7 @@ public function __construct(
115116
string $maxSizeMessage = null,
116117
string $mimeTypesMessage = null,
117118
string $disallowEmptyMessage = null,
119+
string $filenameTooLongMessage = null,
118120
string $uploadIniSizeErrorMessage = null,
119121
string $uploadFormSizeErrorMessage = null,
120122
string $uploadPartialErrorMessage = null,
@@ -144,11 +146,13 @@ public function __construct(
144146
$maxSize,
145147
$binaryFormat,
146148
$mimeTypes,
149+
$filenameMaxLength,
147150
$notFoundMessage,
148151
$notReadableMessage,
149152
$maxSizeMessage,
150153
$mimeTypesMessage,
151154
$disallowEmptyMessage,
155+
$filenameTooLongMessage,
152156
$uploadIniSizeErrorMessage,
153157
$uploadFormSizeErrorMessage,
154158
$uploadPartialErrorMessage,

src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTestCase.php

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ public function testNegativeMaxSize()
531531
}
532532

533533
/**
534-
* @dataProvider validExtensionProvider
534+
* @dataProvider providerValidExtension
535535
*/
536536
public function testExtensionValid(string $name)
537537
{
@@ -551,7 +551,7 @@ public function testExtensionValid(string $name)
551551
$this->assertNoViolation();
552552
}
553553

554-
private static function validExtensionProvider(): iterable
554+
public static function providerValidExtension(): iterable
555555
{
556556
yield ['test.gif'];
557557
yield ['test.png.gif'];
@@ -560,7 +560,7 @@ private static function validExtensionProvider(): iterable
560560
}
561561

562562
/**
563-
* @dataProvider invalidExtensionProvider
563+
* @dataProvider provideInvalidExtension
564564
*/
565565
public function testExtensionInvalid(string $name, string $extension)
566566
{
@@ -582,7 +582,7 @@ public function testExtensionInvalid(string $name, string $extension)
582582
->assertRaised();
583583
}
584584

585-
private static function invalidExtensionProvider(): iterable
585+
public static function provideInvalidExtension(): iterable
586586
{
587587
yield ['test.gif', 'gif'];
588588
yield ['test.png.gif', 'gif'];
@@ -658,5 +658,46 @@ public function testUploadedFileExtensions()
658658
$this->assertNoViolation();
659659
}
660660

661+
/**
662+
* @dataProvider provideFilenameMaxLengthIsTooLong
663+
*/
664+
public function testFilenameMaxLengthIsTooLong(File $constraintFile, string $messageViolation)
665+
{
666+
file_put_contents($this->path, '1');
667+
668+
$file = new UploadedFile($this->path, 'myFileWithATooLongOriginalFileName', null, null, true);
669+
$this->validator->validate($file, $constraintFile);
670+
671+
$this->buildViolation($messageViolation)
672+
->setParameters([
673+
'{{ filename_max_length }}' => $constraintFile->nameMaxLength,
674+
])
675+
->setCode(File::FILENAME_TOO_LONG)
676+
->assertRaised();
677+
}
678+
679+
public static function provideFilenameMaxLengthIsTooLong(): \Generator
680+
{
681+
yield 'Simple case with only the parameter "filenameMaxLength" ' => [
682+
new File(filenameMaxLength: 30),
683+
'The filename is too long. It should have {{ filename_max_length }} characters or less.',
684+
];
685+
686+
yield 'Case with the parameter "filenameMaxLength" and a custom error message' => [
687+
new File(filenameMaxLength: 20, filenameTooLongMessage: 'Your filename is too long. Please use at maximum {{ filename_max_length }} characters'),
688+
'Your filename is too long. Please use at maximum {{ filename_max_length }} characters',
689+
];
690+
}
691+
692+
public function testFilenameMaxLength()
693+
{
694+
file_put_contents($this->path, '1');
695+
696+
$file = new UploadedFile($this->path, 'tinyOriginalFileName', null, null, true);
697+
$this->validator->validate($file, new File(filenameMaxLength: 20));
698+
699+
$this->assertNoViolation();
700+
}
701+
661702
abstract protected function getFile($filename);
662703
}

0 commit comments

Comments
 (0)