ImageValidator.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator\Constraints;
  11. use Symfony\Component\Validator\Constraint;
  12. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  13. use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
  14. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  15. /**
  16. * Validates whether a value is a valid image file and is valid
  17. * against minWidth, maxWidth, minHeight and maxHeight constraints.
  18. *
  19. * @author Benjamin Dulau <benjamin.dulau@gmail.com>
  20. * @author Bernhard Schussek <bschussek@gmail.com>
  21. */
  22. class ImageValidator extends FileValidator
  23. {
  24. /**
  25. * {@inheritdoc}
  26. */
  27. public function validate($value, Constraint $constraint)
  28. {
  29. if (!$constraint instanceof Image) {
  30. throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Image');
  31. }
  32. $violations = \count($this->context->getViolations());
  33. parent::validate($value, $constraint);
  34. $failed = \count($this->context->getViolations()) !== $violations;
  35. if ($failed || null === $value || '' === $value) {
  36. return;
  37. }
  38. if (null === $constraint->minWidth && null === $constraint->maxWidth
  39. && null === $constraint->minHeight && null === $constraint->maxHeight
  40. && null === $constraint->minRatio && null === $constraint->maxRatio
  41. && $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait) {
  42. return;
  43. }
  44. $size = @getimagesize($value);
  45. if (empty($size) || (0 === $size[0]) || (0 === $size[1])) {
  46. if ($this->context instanceof ExecutionContextInterface) {
  47. $this->context->buildViolation($constraint->sizeNotDetectedMessage)
  48. ->setCode(Image::SIZE_NOT_DETECTED_ERROR)
  49. ->addViolation();
  50. } else {
  51. $this->buildViolation($constraint->sizeNotDetectedMessage)
  52. ->setCode(Image::SIZE_NOT_DETECTED_ERROR)
  53. ->addViolation();
  54. }
  55. return;
  56. }
  57. $width = $size[0];
  58. $height = $size[1];
  59. if ($constraint->minWidth) {
  60. if (!ctype_digit((string) $constraint->minWidth)) {
  61. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum width', $constraint->minWidth));
  62. }
  63. if ($width < $constraint->minWidth) {
  64. if ($this->context instanceof ExecutionContextInterface) {
  65. $this->context->buildViolation($constraint->minWidthMessage)
  66. ->setParameter('{{ width }}', $width)
  67. ->setParameter('{{ min_width }}', $constraint->minWidth)
  68. ->setCode(Image::TOO_NARROW_ERROR)
  69. ->addViolation();
  70. } else {
  71. $this->buildViolation($constraint->minWidthMessage)
  72. ->setParameter('{{ width }}', $width)
  73. ->setParameter('{{ min_width }}', $constraint->minWidth)
  74. ->setCode(Image::TOO_NARROW_ERROR)
  75. ->addViolation();
  76. }
  77. return;
  78. }
  79. }
  80. if ($constraint->maxWidth) {
  81. if (!ctype_digit((string) $constraint->maxWidth)) {
  82. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum width', $constraint->maxWidth));
  83. }
  84. if ($width > $constraint->maxWidth) {
  85. if ($this->context instanceof ExecutionContextInterface) {
  86. $this->context->buildViolation($constraint->maxWidthMessage)
  87. ->setParameter('{{ width }}', $width)
  88. ->setParameter('{{ max_width }}', $constraint->maxWidth)
  89. ->setCode(Image::TOO_WIDE_ERROR)
  90. ->addViolation();
  91. } else {
  92. $this->buildViolation($constraint->maxWidthMessage)
  93. ->setParameter('{{ width }}', $width)
  94. ->setParameter('{{ max_width }}', $constraint->maxWidth)
  95. ->setCode(Image::TOO_WIDE_ERROR)
  96. ->addViolation();
  97. }
  98. return;
  99. }
  100. }
  101. if ($constraint->minHeight) {
  102. if (!ctype_digit((string) $constraint->minHeight)) {
  103. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum height', $constraint->minHeight));
  104. }
  105. if ($height < $constraint->minHeight) {
  106. if ($this->context instanceof ExecutionContextInterface) {
  107. $this->context->buildViolation($constraint->minHeightMessage)
  108. ->setParameter('{{ height }}', $height)
  109. ->setParameter('{{ min_height }}', $constraint->minHeight)
  110. ->setCode(Image::TOO_LOW_ERROR)
  111. ->addViolation();
  112. } else {
  113. $this->buildViolation($constraint->minHeightMessage)
  114. ->setParameter('{{ height }}', $height)
  115. ->setParameter('{{ min_height }}', $constraint->minHeight)
  116. ->setCode(Image::TOO_LOW_ERROR)
  117. ->addViolation();
  118. }
  119. return;
  120. }
  121. }
  122. if ($constraint->maxHeight) {
  123. if (!ctype_digit((string) $constraint->maxHeight)) {
  124. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum height', $constraint->maxHeight));
  125. }
  126. if ($height > $constraint->maxHeight) {
  127. if ($this->context instanceof ExecutionContextInterface) {
  128. $this->context->buildViolation($constraint->maxHeightMessage)
  129. ->setParameter('{{ height }}', $height)
  130. ->setParameter('{{ max_height }}', $constraint->maxHeight)
  131. ->setCode(Image::TOO_HIGH_ERROR)
  132. ->addViolation();
  133. } else {
  134. $this->buildViolation($constraint->maxHeightMessage)
  135. ->setParameter('{{ height }}', $height)
  136. ->setParameter('{{ max_height }}', $constraint->maxHeight)
  137. ->setCode(Image::TOO_HIGH_ERROR)
  138. ->addViolation();
  139. }
  140. }
  141. }
  142. $ratio = round($width / $height, 2);
  143. if (null !== $constraint->minRatio) {
  144. if (!is_numeric((string) $constraint->minRatio)) {
  145. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid minimum ratio', $constraint->minRatio));
  146. }
  147. if ($ratio < $constraint->minRatio) {
  148. if ($this->context instanceof ExecutionContextInterface) {
  149. $this->context->buildViolation($constraint->minRatioMessage)
  150. ->setParameter('{{ ratio }}', $ratio)
  151. ->setParameter('{{ min_ratio }}', $constraint->minRatio)
  152. ->setCode(Image::RATIO_TOO_SMALL_ERROR)
  153. ->addViolation();
  154. } else {
  155. $this->buildViolation($constraint->minRatioMessage)
  156. ->setParameter('{{ ratio }}', $ratio)
  157. ->setParameter('{{ min_ratio }}', $constraint->minRatio)
  158. ->setCode(Image::RATIO_TOO_SMALL_ERROR)
  159. ->addViolation();
  160. }
  161. }
  162. }
  163. if (null !== $constraint->maxRatio) {
  164. if (!is_numeric((string) $constraint->maxRatio)) {
  165. throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum ratio', $constraint->maxRatio));
  166. }
  167. if ($ratio > $constraint->maxRatio) {
  168. if ($this->context instanceof ExecutionContextInterface) {
  169. $this->context->buildViolation($constraint->maxRatioMessage)
  170. ->setParameter('{{ ratio }}', $ratio)
  171. ->setParameter('{{ max_ratio }}', $constraint->maxRatio)
  172. ->setCode(Image::RATIO_TOO_BIG_ERROR)
  173. ->addViolation();
  174. } else {
  175. $this->buildViolation($constraint->maxRatioMessage)
  176. ->setParameter('{{ ratio }}', $ratio)
  177. ->setParameter('{{ max_ratio }}', $constraint->maxRatio)
  178. ->setCode(Image::RATIO_TOO_BIG_ERROR)
  179. ->addViolation();
  180. }
  181. }
  182. }
  183. if (!$constraint->allowSquare && $width == $height) {
  184. if ($this->context instanceof ExecutionContextInterface) {
  185. $this->context->buildViolation($constraint->allowSquareMessage)
  186. ->setParameter('{{ width }}', $width)
  187. ->setParameter('{{ height }}', $height)
  188. ->setCode(Image::SQUARE_NOT_ALLOWED_ERROR)
  189. ->addViolation();
  190. } else {
  191. $this->buildViolation($constraint->allowSquareMessage)
  192. ->setParameter('{{ width }}', $width)
  193. ->setParameter('{{ height }}', $height)
  194. ->setCode(Image::SQUARE_NOT_ALLOWED_ERROR)
  195. ->addViolation();
  196. }
  197. }
  198. if (!$constraint->allowLandscape && $width > $height) {
  199. if ($this->context instanceof ExecutionContextInterface) {
  200. $this->context->buildViolation($constraint->allowLandscapeMessage)
  201. ->setParameter('{{ width }}', $width)
  202. ->setParameter('{{ height }}', $height)
  203. ->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR)
  204. ->addViolation();
  205. } else {
  206. $this->buildViolation($constraint->allowLandscapeMessage)
  207. ->setParameter('{{ width }}', $width)
  208. ->setParameter('{{ height }}', $height)
  209. ->setCode(Image::LANDSCAPE_NOT_ALLOWED_ERROR)
  210. ->addViolation();
  211. }
  212. }
  213. if (!$constraint->allowPortrait && $width < $height) {
  214. if ($this->context instanceof ExecutionContextInterface) {
  215. $this->context->buildViolation($constraint->allowPortraitMessage)
  216. ->setParameter('{{ width }}', $width)
  217. ->setParameter('{{ height }}', $height)
  218. ->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR)
  219. ->addViolation();
  220. } else {
  221. $this->buildViolation($constraint->allowPortraitMessage)
  222. ->setParameter('{{ width }}', $width)
  223. ->setParameter('{{ height }}', $height)
  224. ->setCode(Image::PORTRAIT_NOT_ALLOWED_ERROR)
  225. ->addViolation();
  226. }
  227. }
  228. }
  229. }