diff options
Diffstat (limited to 'framework/validators/CFileValidator.php')
| -rw-r--r-- | framework/validators/CFileValidator.php | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/framework/validators/CFileValidator.php b/framework/validators/CFileValidator.php new file mode 100644 index 0000000..d90d70e --- /dev/null +++ b/framework/validators/CFileValidator.php @@ -0,0 +1,251 @@ +<?php +/** + * CFileValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFileValidator verifies if an attribute is receiving a valid uploaded file. + * + * It uses the model class and attribute name to retrieve the information + * about the uploaded file. It then checks if a file is uploaded successfully, + * if the file size is within the limit and if the file type is allowed. + * + * This validator will attempt to fetch uploaded data if attribute is not + * previously set. Please note that this cannot be done if input is tabular: + * <pre> + * foreach($models as $i=>$model) + * $model->attribute = CUploadedFile::getInstance($model, "[$i]attribute"); + * </pre> + * Please note that you must use {@link CUploadedFile::getInstances} for multiple + * file uploads. + * + * When using CFileValidator with an active record, the following code is often used: + * <pre> + * if($model->save()) + * { + * // single upload + * $model->attribute->saveAs($path); + * // multiple upload + * foreach($model->attribute as $file) + * $file->saveAs($path); + * } + * </pre> + * + * You can use {@link CFileValidator} to validate the file attribute. + * + * In addition to the {@link message} property for setting a custom error message, + * CFileValidator has a few custom error messages you can set that correspond to different + * validation scenarios. When the file is too large, you may use the {@link tooLarge} property + * to define a custom error message. Similarly for {@link tooSmall}, {@link wrongType} and + * {@link tooMany}. The messages may contain additional placeholders that will be replaced + * with the actual content. In addition to the "{attribute}" placeholder, recognized by all + * validators (see {@link CValidator}), CFileValidator allows for the following placeholders + * to be specified: + * <ul> + * <li>{file}: replaced with the name of the file.</li> + * <li>{limit}: when using {@link tooLarge}, replaced with {@link maxSize}; + * when using {@link tooSmall}, replaced with {@link maxSize}; and when using {@link tooMany} + * replaced with {@link maxFiles}.</li> + * <li>{extensions}: when using {@link wrongType}, it will be replaced with the allowed extensions.</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CFileValidator.php 3491 2011-12-17 05:17:57Z jefftulsa $ + * @package system.validators + * @since 1.0 + */ +class CFileValidator extends CValidator +{ + /** + * @var boolean whether the attribute requires a file to be uploaded or not. + * Defaults to false, meaning a file is required to be uploaded. + */ + public $allowEmpty=false; + /** + * @var mixed a list of file name extensions that are allowed to be uploaded. + * This can be either an array or a string consisting of file extension names + * separated by space or comma (e.g. "gif, jpg"). + * Extension names are case-insensitive. Defaults to null, meaning all file name + * extensions are allowed. + */ + public $types; + /** + * @var integer the minimum number of bytes required for the uploaded file. + * Defaults to null, meaning no limit. + * @see tooSmall + */ + public $minSize; + /** + * @var integer the maximum number of bytes required for the uploaded file. + * Defaults to null, meaning no limit. + * Note, the size limit is also affected by 'upload_max_filesize' INI setting + * and the 'MAX_FILE_SIZE' hidden field value. + * @see tooLarge + */ + public $maxSize; + /** + * @var string the error message used when the uploaded file is too large. + * @see maxSize + */ + public $tooLarge; + /** + * @var string the error message used when the uploaded file is too small. + * @see minSize + */ + public $tooSmall; + /** + * @var string the error message used when the uploaded file has an extension name + * that is not listed among {@link extensions}. + */ + public $wrongType; + /** + * @var integer the maximum file count the given attribute can hold. + * It defaults to 1, meaning single file upload. By defining a higher number, + * multiple uploads become possible. + */ + public $maxFiles=1; + /** + * @var string the error message used if the count of multiple uploads exceeds + * limit. + */ + public $tooMany; + + /** + * Set the attribute and then validates using {@link validateFile}. + * If there is any error, the error message is added to the object. + * @param CModel $object the object being validated + * @param string $attribute the attribute being validated + */ + protected function validateAttribute($object, $attribute) + { + if($this->maxFiles > 1) + { + $files=$object->$attribute; + if(!is_array($files) || !isset($files[0]) || !$files[0] instanceof CUploadedFile) + $files = CUploadedFile::getInstances($object, $attribute); + if(array()===$files) + return $this->emptyAttribute($object, $attribute); + if(count($files) > $this->maxFiles) + { + $message=$this->tooMany!==null?$this->tooMany : Yii::t('yii', '{attribute} cannot accept more than {limit} files.'); + $this->addError($object, $attribute, $message, array('{attribute}'=>$attribute, '{limit}'=>$this->maxFiles)); + } + else + foreach($files as $file) + $this->validateFile($object, $attribute, $file); + } + else + { + $file = $object->$attribute; + if(!$file instanceof CUploadedFile) + { + $file = CUploadedFile::getInstance($object, $attribute); + if(null===$file) + return $this->emptyAttribute($object, $attribute); + } + $this->validateFile($object, $attribute, $file); + } + } + + /** + * Internally validates a file object. + * @param CModel $object the object being validated + * @param string $attribute the attribute being validated + * @param CUploadedFile $file uploaded file passed to check against a set of rules + */ + protected function validateFile($object, $attribute, $file) + { + if(null===$file || ($error=$file->getError())==UPLOAD_ERR_NO_FILE) + return $this->emptyAttribute($object, $attribute); + else if($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize) + { + $message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file "{file}" is too large. Its size cannot exceed {limit} bytes.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->getSizeLimit())); + } + else if($error==UPLOAD_ERR_PARTIAL) + throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>$file->getName()))); + else if($error==UPLOAD_ERR_NO_TMP_DIR) + throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>$file->getName()))); + else if($error==UPLOAD_ERR_CANT_WRITE) + throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>$file->getName()))); + else if(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION) // available for PHP 5.2.0 or above + throw new CException(Yii::t('yii','File upload was stopped by extension.')); + + if($this->minSize!==null && $file->getSize()<$this->minSize) + { + $message=$this->tooSmall!==null?$this->tooSmall : Yii::t('yii','The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->minSize)); + } + + if($this->types!==null) + { + if(is_string($this->types)) + $types=preg_split('/[\s,]+/',strtolower($this->types),-1,PREG_SPLIT_NO_EMPTY); + else + $types=$this->types; + if(!in_array(strtolower($file->getExtensionName()),$types)) + { + $message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.'); + $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types))); + } + } + } + + /** + * Raises an error to inform end user about blank attribute. + * @param CModel $object the object being validated + * @param string $attribute the attribute being validated + */ + protected function emptyAttribute($object, $attribute) + { + if(!$this->allowEmpty) + { + $message=$this->message!==null?$this->message : Yii::t('yii','{attribute} cannot be blank.'); + $this->addError($object,$attribute,$message); + } + } + + /** + * Returns the maximum size allowed for uploaded files. + * This is determined based on three factors: + * <ul> + * <li>'upload_max_filesize' in php.ini</li> + * <li>'MAX_FILE_SIZE' hidden field</li> + * <li>{@link maxSize}</li> + * </ul> + * + * @return integer the size limit for uploaded files. + */ + protected function getSizeLimit() + { + $limit=ini_get('upload_max_filesize'); + $limit=$this->sizeToBytes($limit); + if($this->maxSize!==null && $limit>0 && $this->maxSize<$limit) + $limit=$this->maxSize; + if(isset($_POST['MAX_FILE_SIZE']) && $_POST['MAX_FILE_SIZE']>0 && $_POST['MAX_FILE_SIZE']<$limit) + $limit=$_POST['MAX_FILE_SIZE']; + return $limit; + } + + /** + * Converts php.ini style size to bytes + * + * @param string $sizeStr $sizeStr + * @return int + */ + private function sizeToBytes($sizeStr) + { + switch (substr($sizeStr, -1)) + { + case 'M': case 'm': return (int)$sizeStr * 1048576; + case 'K': case 'k': return (int)$sizeStr * 1024; + case 'G': case 'g': return (int)$sizeStr * 1073741824; + default: return (int)$sizeStr; + } + } +}
\ No newline at end of file |
