1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Validator\File; 11 12use Zend\Stdlib\ErrorHandler; 13use Zend\Validator\AbstractValidator; 14use Zend\Validator\Exception; 15 16/** 17 * Validator for the image size of an image file 18 */ 19class ImageSize extends AbstractValidator 20{ 21 /** 22 * @const string Error constants 23 */ 24 const WIDTH_TOO_BIG = 'fileImageSizeWidthTooBig'; 25 const WIDTH_TOO_SMALL = 'fileImageSizeWidthTooSmall'; 26 const HEIGHT_TOO_BIG = 'fileImageSizeHeightTooBig'; 27 const HEIGHT_TOO_SMALL = 'fileImageSizeHeightTooSmall'; 28 const NOT_DETECTED = 'fileImageSizeNotDetected'; 29 const NOT_READABLE = 'fileImageSizeNotReadable'; 30 31 /** 32 * @var array Error message template 33 */ 34 protected $messageTemplates = array( 35 self::WIDTH_TOO_BIG => "Maximum allowed width for image should be '%maxwidth%' but '%width%' detected", 36 self::WIDTH_TOO_SMALL => "Minimum expected width for image should be '%minwidth%' but '%width%' detected", 37 self::HEIGHT_TOO_BIG => "Maximum allowed height for image should be '%maxheight%' but '%height%' detected", 38 self::HEIGHT_TOO_SMALL => "Minimum expected height for image should be '%minheight%' but '%height%' detected", 39 self::NOT_DETECTED => "The size of image could not be detected", 40 self::NOT_READABLE => "File is not readable or does not exist", 41 ); 42 43 /** 44 * @var array Error message template variables 45 */ 46 protected $messageVariables = array( 47 'minwidth' => array('options' => 'minWidth'), 48 'maxwidth' => array('options' => 'maxWidth'), 49 'minheight' => array('options' => 'minHeight'), 50 'maxheight' => array('options' => 'maxHeight'), 51 'width' => 'width', 52 'height' => 'height' 53 ); 54 55 /** 56 * Detected width 57 * 58 * @var int 59 */ 60 protected $width; 61 62 /** 63 * Detected height 64 * 65 * @var int 66 */ 67 protected $height; 68 69 /** 70 * Options for this validator 71 * 72 * @var array 73 */ 74 protected $options = array( 75 'minWidth' => null, // Minimum image width 76 'maxWidth' => null, // Maximum image width 77 'minHeight' => null, // Minimum image height 78 'maxHeight' => null, // Maximum image height 79 ); 80 81 /** 82 * Sets validator options 83 * 84 * Accepts the following option keys: 85 * - minheight 86 * - minwidth 87 * - maxheight 88 * - maxwidth 89 * 90 * @param array|\Traversable $options 91 */ 92 public function __construct($options = null) 93 { 94 if (1 < func_num_args()) { 95 if (!is_array($options)) { 96 $options = array('minWidth' => $options); 97 } 98 99 $argv = func_get_args(); 100 array_shift($argv); 101 $options['minHeight'] = array_shift($argv); 102 if (!empty($argv)) { 103 $options['maxWidth'] = array_shift($argv); 104 if (!empty($argv)) { 105 $options['maxHeight'] = array_shift($argv); 106 } 107 } 108 } 109 110 parent::__construct($options); 111 } 112 113 /** 114 * Returns the minimum allowed width 115 * 116 * @return int 117 */ 118 public function getMinWidth() 119 { 120 return $this->options['minWidth']; 121 } 122 123 /** 124 * Sets the minimum allowed width 125 * 126 * @param int $minWidth 127 * @return ImageSize Provides a fluid interface 128 * @throws Exception\InvalidArgumentException When minwidth is greater than maxwidth 129 */ 130 public function setMinWidth($minWidth) 131 { 132 if (($this->getMaxWidth() !== null) && ($minWidth > $this->getMaxWidth())) { 133 throw new Exception\InvalidArgumentException( 134 "The minimum image width must be less than or equal to the " 135 . " maximum image width, but {$minWidth} > {$this->getMaxWidth()}" 136 ); 137 } 138 139 $this->options['minWidth'] = (int) $minWidth; 140 return $this; 141 } 142 143 /** 144 * Returns the maximum allowed width 145 * 146 * @return int 147 */ 148 public function getMaxWidth() 149 { 150 return $this->options['maxWidth']; 151 } 152 153 /** 154 * Sets the maximum allowed width 155 * 156 * @param int $maxWidth 157 * @return ImageSize Provides a fluid interface 158 * @throws Exception\InvalidArgumentException When maxwidth is less than minwidth 159 */ 160 public function setMaxWidth($maxWidth) 161 { 162 if (($this->getMinWidth() !== null) && ($maxWidth < $this->getMinWidth())) { 163 throw new Exception\InvalidArgumentException( 164 "The maximum image width must be greater than or equal to the " 165 . "minimum image width, but {$maxWidth} < {$this->getMinWidth()}" 166 ); 167 } 168 169 $this->options['maxWidth'] = (int) $maxWidth; 170 return $this; 171 } 172 173 /** 174 * Returns the minimum allowed height 175 * 176 * @return int 177 */ 178 public function getMinHeight() 179 { 180 return $this->options['minHeight']; 181 } 182 183 /** 184 * Sets the minimum allowed height 185 * 186 * @param int $minHeight 187 * @return ImageSize Provides a fluid interface 188 * @throws Exception\InvalidArgumentException When minheight is greater than maxheight 189 */ 190 public function setMinHeight($minHeight) 191 { 192 if (($this->getMaxHeight() !== null) && ($minHeight > $this->getMaxHeight())) { 193 throw new Exception\InvalidArgumentException( 194 "The minimum image height must be less than or equal to the " 195 . " maximum image height, but {$minHeight} > {$this->getMaxHeight()}" 196 ); 197 } 198 199 $this->options['minHeight'] = (int) $minHeight; 200 return $this; 201 } 202 203 /** 204 * Returns the maximum allowed height 205 * 206 * @return int 207 */ 208 public function getMaxHeight() 209 { 210 return $this->options['maxHeight']; 211 } 212 213 /** 214 * Sets the maximum allowed height 215 * 216 * @param int $maxHeight 217 * @return ImageSize Provides a fluid interface 218 * @throws Exception\InvalidArgumentException When maxheight is less than minheight 219 */ 220 public function setMaxHeight($maxHeight) 221 { 222 if (($this->getMinHeight() !== null) && ($maxHeight < $this->getMinHeight())) { 223 throw new Exception\InvalidArgumentException( 224 "The maximum image height must be greater than or equal to the " 225 . "minimum image height, but {$maxHeight} < {$this->getMinHeight()}" 226 ); 227 } 228 229 $this->options['maxHeight'] = (int) $maxHeight; 230 return $this; 231 } 232 233 /** 234 * Returns the set minimum image sizes 235 * 236 * @return array 237 */ 238 public function getImageMin() 239 { 240 return array('minWidth' => $this->getMinWidth(), 'minHeight' => $this->getMinHeight()); 241 } 242 243 /** 244 * Returns the set maximum image sizes 245 * 246 * @return array 247 */ 248 public function getImageMax() 249 { 250 return array('maxWidth' => $this->getMaxWidth(), 'maxHeight' => $this->getMaxHeight()); 251 } 252 253 /** 254 * Returns the set image width sizes 255 * 256 * @return array 257 */ 258 public function getImageWidth() 259 { 260 return array('minWidth' => $this->getMinWidth(), 'maxWidth' => $this->getMaxWidth()); 261 } 262 263 /** 264 * Returns the set image height sizes 265 * 266 * @return array 267 */ 268 public function getImageHeight() 269 { 270 return array('minHeight' => $this->getMinHeight(), 'maxHeight' => $this->getMaxHeight()); 271 } 272 273 /** 274 * Sets the minimum image size 275 * 276 * @param array $options The minimum image dimensions 277 * @return ImageSize Provides a fluent interface 278 */ 279 public function setImageMin($options) 280 { 281 $this->setOptions($options); 282 return $this; 283 } 284 285 /** 286 * Sets the maximum image size 287 * 288 * @param array|\Traversable $options The maximum image dimensions 289 * @return ImageSize Provides a fluent interface 290 */ 291 public function setImageMax($options) 292 { 293 $this->setOptions($options); 294 return $this; 295 } 296 297 /** 298 * Sets the minimum and maximum image width 299 * 300 * @param array $options The image width dimensions 301 * @return ImageSize Provides a fluent interface 302 */ 303 public function setImageWidth($options) 304 { 305 $this->setImageMin($options); 306 $this->setImageMax($options); 307 308 return $this; 309 } 310 311 /** 312 * Sets the minimum and maximum image height 313 * 314 * @param array $options The image height dimensions 315 * @return ImageSize Provides a fluent interface 316 */ 317 public function setImageHeight($options) 318 { 319 $this->setImageMin($options); 320 $this->setImageMax($options); 321 322 return $this; 323 } 324 325 /** 326 * Returns true if and only if the image size of $value is at least min and 327 * not bigger than max 328 * 329 * @param string|array $value Real file to check for image size 330 * @param array $file File data from \Zend\File\Transfer\Transfer (optional) 331 * @return bool 332 */ 333 public function isValid($value, $file = null) 334 { 335 if (is_string($value) && is_array($file)) { 336 // Legacy Zend\Transfer API support 337 $filename = $file['name']; 338 $file = $file['tmp_name']; 339 } elseif (is_array($value)) { 340 if (!isset($value['tmp_name']) || !isset($value['name'])) { 341 throw new Exception\InvalidArgumentException( 342 'Value array must be in $_FILES format' 343 ); 344 } 345 $file = $value['tmp_name']; 346 $filename = $value['name']; 347 } else { 348 $file = $value; 349 $filename = basename($file); 350 } 351 $this->setValue($filename); 352 353 // Is file readable ? 354 if (empty($file) || false === stream_resolve_include_path($file)) { 355 $this->error(self::NOT_READABLE); 356 return false; 357 } 358 359 ErrorHandler::start(); 360 $size = getimagesize($file); 361 ErrorHandler::stop(); 362 363 if (empty($size) || ($size[0] === 0) || ($size[1] === 0)) { 364 $this->error(self::NOT_DETECTED); 365 return false; 366 } 367 368 $this->width = $size[0]; 369 $this->height = $size[1]; 370 if ($this->width < $this->getMinWidth()) { 371 $this->error(self::WIDTH_TOO_SMALL); 372 } 373 374 if (($this->getMaxWidth() !== null) && ($this->getMaxWidth() < $this->width)) { 375 $this->error(self::WIDTH_TOO_BIG); 376 } 377 378 if ($this->height < $this->getMinHeight()) { 379 $this->error(self::HEIGHT_TOO_SMALL); 380 } 381 382 if (($this->getMaxHeight() !== null) && ($this->getMaxHeight() < $this->height)) { 383 $this->error(self::HEIGHT_TOO_BIG); 384 } 385 386 if (count($this->getMessages()) > 0) { 387 return false; 388 } 389 390 return true; 391 } 392} 393