summaryrefslogtreecommitdiff
path: root/framework/validators/CFileValidator.php
diff options
context:
space:
mode:
Diffstat (limited to 'framework/validators/CFileValidator.php')
-rw-r--r--framework/validators/CFileValidator.php251
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 &copy; 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