Skip to content

Commit 2cc36f1

Browse files
committed
merged branch lmammino/improved-image-validator (PR #8490)
This PR was squashed before being merged into the master branch (closes #8490). Discussion ---------- [Validator] improved image validator | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | symfony/symfony-docs#2840 CHANGELOG * Added options to validate image aspect ratio (`minRatio` and `maxRatio`) * Added options to validate if the image ratio is square, landscape or portrait (`allowSquare`, `allowLandscape`, and `allowPortrait`) Commits ------- b030624 [Validator] improved image validator
2 parents fbc9082 + b030624 commit 2cc36f1

File tree

6 files changed

+209
-1
lines changed

6 files changed

+209
-1
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
2.4.0
5+
-----
6+
7+
* added `minRatio`, `maxRatio`, `allowSquare`, `allowLandscape`, and `allowPortrait` to Image validator
8+
49
2.3.0
510
-----
611

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,21 @@ class Image extends File
2323
public $maxWidth = null;
2424
public $maxHeight = null;
2525
public $minHeight = null;
26+
public $maxRatio = null;
27+
public $minRatio = null;
28+
public $allowSquare = true;
29+
public $allowLandscape = true;
30+
public $allowPortrait = true;
2631

2732
public $mimeTypesMessage = 'This file is not a valid image.';
2833
public $sizeNotDetectedMessage = 'The size of the image could not be detected.';
2934
public $maxWidthMessage = 'The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.';
3035
public $minWidthMessage = 'The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.';
3136
public $maxHeightMessage = 'The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.';
3237
public $minHeightMessage = 'The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.';
38+
public $maxRatioMessage = 'The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.';
39+
public $minRatioMessage = 'The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.';
40+
public $allowSquareMessage = 'The image is square ({{ width }}x{{ height }}px). Square images are not allowed.';
41+
public $allowLandscapeMessage = 'The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.';
42+
public $allowPortraitMessage = 'The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.';
3343
}

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ public function validate($value, Constraint $constraint)
3838
}
3939

4040
if (null === $constraint->minWidth && null === $constraint->maxWidth
41-
&& null === $constraint->minHeight && null === $constraint->maxHeight) {
41+
&& null === $constraint->minHeight && null === $constraint->maxHeight
42+
&& null === $constraint->minRatio && null === $constraint->maxRatio
43+
&& $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait) {
4244
return;
4345
}
4446

@@ -109,5 +111,55 @@ public function validate($value, Constraint $constraint)
109111
));
110112
}
111113
}
114+
115+
$ratio = $width / $height;
116+
117+
if (null !== $constraint->minRatio) {
118+
if (!is_numeric((string) $constraint->minRatio)) {
119+
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum ratio', $constraint->minRatio));
120+
}
121+
122+
if ($ratio < $constraint->minRatio) {
123+
$this->context->addViolation($constraint->minRatioMessage, array(
124+
'{{ ratio }}' => $ratio,
125+
'{{ min_ratio }}' => $constraint->minRatio
126+
));
127+
}
128+
}
129+
130+
if (null !== $constraint->maxRatio) {
131+
if (!is_numeric((string) $constraint->maxRatio)) {
132+
throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum ratio', $constraint->maxRatio));
133+
}
134+
135+
if ($ratio > $constraint->maxRatio) {
136+
$this->context->addViolation($constraint->maxRatioMessage, array(
137+
'{{ ratio }}' => $ratio,
138+
'{{ max_ratio }}' => $constraint->maxRatio
139+
));
140+
}
141+
}
142+
143+
if (!$constraint->allowSquare && $width == $height) {
144+
$this->context->addViolation($constraint->allowSquareMessage, array(
145+
'{{ width }}' => $width,
146+
'{{ height }}' => $height
147+
));
148+
}
149+
150+
if (!$constraint->allowLandscape && $width > $height) {
151+
$this->context->addViolation($constraint->allowLandscapeMessage, array(
152+
'{{ width }}' => $width,
153+
'{{ height }}' => $height
154+
));
155+
}
156+
157+
if (!$constraint->allowPortrait && $width < $height) {
158+
$this->context->addViolation($constraint->allowPortraitMessage, array(
159+
'{{ width }}' => $width,
160+
'{{ height }}' => $height
161+
));
162+
}
163+
112164
}
113165
}

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

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ class ImageValidatorTest extends \PHPUnit_Framework_TestCase
2020
protected $validator;
2121
protected $path;
2222
protected $image;
23+
protected $imageLandscape;
24+
protected $imagePortrait;
2325

2426
protected function setUp()
2527
{
2628
$this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
2729
$this->validator = new ImageValidator();
2830
$this->validator->initialize($this->context);
2931
$this->image = __DIR__.'/Fixtures/test.gif';
32+
$this->imageLandscape = __DIR__.'/Fixtures/test_landscape.gif';
33+
$this->imagePortrait = __DIR__.'/Fixtures/test_portrait.gif';
3034
}
3135

3236
public function testNullIsValid()
@@ -223,4 +227,141 @@ public function testInvalidMaxHeight()
223227

224228
$this->validator->validate($this->image, $constraint);
225229
}
230+
231+
public function testRatioTooSmall()
232+
{
233+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
234+
$this->markTestSkipped('The "HttpFoundation" component is not available');
235+
}
236+
237+
$constraint = new Image(array(
238+
'minRatio' => 2,
239+
'minRatioMessage' => 'myMessage',
240+
));
241+
242+
$this->context->expects($this->once())
243+
->method('addViolation')
244+
->with('myMessage', array(
245+
'{{ ratio }}' => 1,
246+
'{{ min_ratio }}' => 2,
247+
));
248+
249+
$this->validator->validate($this->image, $constraint);
250+
}
251+
252+
public function testRatioTooBig()
253+
{
254+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
255+
$this->markTestSkipped('The "HttpFoundation" component is not available');
256+
}
257+
258+
$constraint = new Image(array(
259+
'maxRatio' => 0.5,
260+
'maxRatioMessage' => 'myMessage',
261+
));
262+
263+
$this->context->expects($this->once())
264+
->method('addViolation')
265+
->with('myMessage', array(
266+
'{{ ratio }}' => 1,
267+
'{{ max_ratio }}' => 0.5,
268+
));
269+
270+
$this->validator->validate($this->image, $constraint);
271+
}
272+
273+
/**
274+
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
275+
*/
276+
public function testInvalidMinRatio()
277+
{
278+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
279+
$this->markTestSkipped('The "HttpFoundation" component is not available');
280+
}
281+
282+
$constraint = new Image(array(
283+
'minRatio' => '1abc',
284+
));
285+
286+
$this->validator->validate($this->image, $constraint);
287+
}
288+
289+
/**
290+
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
291+
*/
292+
public function testInvalidMaxRatio()
293+
{
294+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
295+
$this->markTestSkipped('The "HttpFoundation" component is not available');
296+
}
297+
298+
$constraint = new Image(array(
299+
'maxRatio' => '1abc',
300+
));
301+
302+
$this->validator->validate($this->image, $constraint);
303+
}
304+
305+
public function testSquareNotAllowed()
306+
{
307+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
308+
$this->markTestSkipped('The "HttpFoundation" component is not available');
309+
}
310+
311+
$constraint = new Image(array(
312+
'allowSquare' => false,
313+
'allowSquareMessage' => 'myMessage',
314+
));
315+
316+
$this->context->expects($this->once())
317+
->method('addViolation')
318+
->with('myMessage', array(
319+
'{{ width }}' => 2,
320+
'{{ height }}' => 2,
321+
));
322+
323+
$this->validator->validate($this->image, $constraint);
324+
}
325+
326+
public function testLandscapeNotAllowed()
327+
{
328+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
329+
$this->markTestSkipped('The "HttpFoundation" component is not available');
330+
}
331+
332+
$constraint = new Image(array(
333+
'allowLandscape' => false,
334+
'allowLandscapeMessage' => 'myMessage',
335+
));
336+
337+
$this->context->expects($this->once())
338+
->method('addViolation')
339+
->with('myMessage', array(
340+
'{{ width }}' => 2,
341+
'{{ height }}' => 1,
342+
));
343+
344+
$this->validator->validate($this->imageLandscape, $constraint);
345+
}
346+
347+
public function testPortraitNotAllowed()
348+
{
349+
if (!class_exists('Symfony\Component\HttpFoundation\File\File')) {
350+
$this->markTestSkipped('The "HttpFoundation" component is not available');
351+
}
352+
353+
$constraint = new Image(array(
354+
'allowPortrait' => false,
355+
'allowPortraitMessage' => 'myMessage',
356+
));
357+
358+
$this->context->expects($this->once())
359+
->method('addViolation')
360+
->with('myMessage', array(
361+
'{{ width }}' => 1,
362+
'{{ height }}' => 2,
363+
));
364+
365+
$this->validator->validate($this->imagePortrait, $constraint);
366+
}
226367
}

0 commit comments

Comments
 (0)