8000 Support SVGs when validating image dimensions · symfony/symfony@a4ace64 · GitHub
[go: up one dir, main page]

Skip to content

Commit a4ace64

Browse files
Support SVGs when validating image dimensions
upadte validator changelog Use Mime Component, Check for corrupted XML
1 parent 08fa74a commit a4ace64

File tree

6 files changed

+138
-2
lines changed

6 files changed

+138
-2
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ CHANGELOG
33

44
6.1
55
---
6-
6+
7+
* Support `SVGs` when validating image dimensions
78
* Deprecate `Constraint::$errorNames`, use `Constraint::ERROR_NAMES` instead
89

910
6.0

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14+
use Symfony\Component\HttpFoundation\File\File as FileObject;
15+
use Symfony\Component\Mime\MimeTypes;
1416
use Symfony\Component\Validator\Constraint;
1517
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1618
use Symfony\Component\Validator\Exception\LogicException;
@@ -53,7 +55,18 @@ public function validate(mixed $value, Constraint $constraint)
5355
return;
5456
}
5557

56-
$size = @getimagesize($value);
58+
if ($this->isSvg($value)) {
59+
$size = $this->getSvgSize($value);
60+
if (null === $size) {
61+
$this->context->buildViolation($constraint->corruptedMessage)
62+
->setCode(Image::CORRUPTED_IMAGE_ERROR)
63+
->addViolation();
64+
65+
return;
66+
}
67+
} else {
68+
$size = @getimagesize($value);
69+
}
5770

5871
if (empty($size) || (0 === $size[0]) || (0 === $size[1])) {
5972
$this->context->buildViolation($constraint->sizeNotDetectedMessage)
@@ -234,4 +247,46 @@ public function validate(mixed $value, Constraint $constraint)
234247
imagedestroy($resource);
235248
}
236249
}
250+
251+
/**
252+
* Check whether a value is an SVG image
253+
*
254+
* @param mixed $value
255+
*
256+
* @return bool
257+
* <b>TRUE</b> if <i>value</i> is an SVG, <b>FALSE</b> if it's not, or if we can't detect its MimeType;
258+
*/
259+
private function isSvg(mixed $value)
260+
{
261+
if ($value instanceof FileObject) {
262+
$mime = $value->getMimeType();
263+
} elseif (class_exists(MimeTypes::class)) {
264+
$mime = MimeTypes::getDefault()->guessMimeType($value);
265+
} elseif (!class_exists(FileObject::class)) {
266+
return false;
267+
} else {
268+
$mime = (new FileObject($value))->getMimeType();
269+
}
270+
271+
return 'image/svg+xml' === $mime;
272+
}
273+
274+
/**
275+
* @param mixed $value
276+
*
277+
* @return array|null
278+
* <b>array</b> contains the width and height of the image
279+
* <b>null</b> if the XML is corrupted
280+
*/
281+
private function getSvgSize(mixed $value)
282+
{
283+
if (false === $xmlValue = simplexml_load_file($value)) {
284+
return null;
285+
}
286+
$svgAttributes = $xmlValue->attributes();
287+
$width = intval($svgAttributes->width);
288+
$height = intval($svgAttributes->height);
289+
290+
return [$width, $height];
291+
}
237292
}
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 6 additions & 0 deletions
Loading

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class ImageValidatorTest extends ConstraintValidatorTestCase
3030
protected $image4By3;
3131
protected $imageCorrupted;
3232
protected $notAnImage;
33+
protected $imageSvg;
34+
protected $imageSvgLandscape;
35+
protected $imageSvgPortrait;
3336

3437
protected function createValidator()
3538
{
@@ -47,6 +50,9 @@ protected function setUp(): void
4750
$this->image16By9 = __DIR__.'/Fixtures/test_16by9.gif';
4851
$this->imageCorrupted = __DIR__.'/Fixtures/test_corrupted.gif';
4952
$this->notAnImage = __DIR__.'/Fixtures/ccc.txt';
53+
$this->imageSvg = __DIR__.'/Fixtures/test_svg.svg';
54+
$this->imageSvgLandscape = __DIR__.'/Fixtures/test_svg_landscape.svg';
55+
$this->imageSvgPortrait = __DIR__.'/Fixtures/test_svg_portrait.svg';
5056
}
5157

5258
public function testNullIsValid()
@@ -536,6 +542,62 @@ public function testInvalidMimeType()
536542
->assertRaised();
537543
}
538544

545+
public function testSvgSize()
546+
{
547+
$constraint = new Image([
548+
'minWidth' => 1,
549+
'maxWidth' => 2,
550+
'minHeight' => 1,
551+
'maxHeight' => 2,
552+
]);
553+
554+
$this->validator->validate($this->imageSvg, $constraint);
555+
556+
$this->assertNoViolation();
557+
}
558+
559+
/**
560+
* @dataProvider provideAllowSquareConstraints
561+
*/
562+
public function testSquareNotAllowedOnSvg(Image $constraint)
563+
{
564+
$this->validator->validate($this->imageSvg, $constraint);
565+
566+
$this->buildViolation('myMessage')
567+
->setParameter('{{ width }}', 1)
568+
->setParameter('{{ height }}', 1)
569+
->setCode(Image::SQUARE_NOT_ALLOWED_ERROR)
570+
->assertRaised();
571+
}
572+
573+
/**
574+
* @dataProvider provideAllowLandscapeConstraints
575+
*/
576+
public function testLandscapeNotAllowedOnSvg(Image $constraint)
577+
{
578+
$this->validator->validate($this->imageSvgLandscape, $constraint);
579+
580+
$this->buildViolation('myMessage')
581+
->setParameter('{{ width }}', 2)
582+
->setParameter('{{ height }}', 1)
583+
->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR)
584+
->assertRaised();
585+
}
586+
587+
/**
588+
* @dataProvider provideAllowPortraitConstraints
589+
*/
590+
public function testPortraitNotAllowedOnSvg(Image $constraint)
591+
{
592+
$this->validator->validate($this->imageSvgPortrait, $constraint);
593+
594+
$this->buildViolation('myMessage')
595+
->setParameter('{{ width }}', 1)
596+
->setParameter('{{ height }}', 2)
597+
->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR)
598+
->assertRaised();
599+
}
600+
539601
public function provideDetectCorruptedConstraints(): iterable
540602
{
541603
yield 'Doctrine style' => [new Image([

0 commit comments

Comments
 (0)
0