Source of file Upload_Validator.php
Size: 10,469 Bytes - Last Modified: 2021-12-23T10:27:40+00:00
/var/www/docs.ssmods.com/process/src/src/Upload_Validator.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 | <?php namespace SilverStripe\Assets; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Injector\Injectable; class Upload_Validator { use Injectable; use Configurable; /** * Contains a list of the max file sizes shared by * all upload fields. This is then duplicated into the * "allowedMaxFileSize" instance property on construct. * * @config * @var array */ private static $default_max_file_size = []; /** * Set to false to assume is_uploaded_file() is true, * Set to true to actually call is_uploaded_file() * Useful to use when testing uploads * * @config * @var bool */ private static $use_is_uploaded_file = true; /** * Information about the temporary file produced * by the PHP-runtime. * * @var array */ protected $tmpFile; protected $errors = []; /** * Restrict filesize for either all filetypes * or a specific extension, with extension-name * as array-key and the size-restriction in bytes as array-value. * * @var array */ public $allowedMaxFileSize = []; /** * @var array Collection of extensions. * Extension-names are treated case-insensitive. * * Example: * <code> * array("jpg","GIF") * </code> */ public $allowedExtensions = []; /** * Return all errors that occurred while validating * the temporary file. * * @return array */ public function getErrors() { return $this->errors; } /** * Clear out all errors */ public function clearErrors() { $this->errors = []; } /** * Set information about temporary file produced by PHP. * @param array $tmpFile */ public function setTmpFile($tmpFile) { $this->tmpFile = $tmpFile; } /** * Returns the largest maximum filesize allowed across all extensions * * @return null|int Filesize in bytes */ public function getLargestAllowedMaxFileSize() { if (!count($this->allowedMaxFileSize)) { return null; } return max(array_values($this->allowedMaxFileSize)); } /** * Get maximum file size for all or specified file extension. * * @param string $ext * @return int Filesize in bytes */ public function getAllowedMaxFileSize($ext = null) { // Check if there is any defined instance max file sizes if (empty($this->allowedMaxFileSize)) { // Set default max file sizes if there isn't $fileSize = Config::inst()->get(__CLASS__, 'default_max_file_size'); if ($fileSize) { $this->setAllowedMaxFileSize($fileSize); } else { // When no default is present, use maximum set by PHP $maxUpload = File::ini2bytes(ini_get('upload_max_filesize')); $maxPost = File::ini2bytes(ini_get('post_max_size')); $this->setAllowedMaxFileSize(min($maxUpload, $maxPost)); } } if ($ext !== null) { $ext = strtolower($ext); if (isset($this->allowedMaxFileSize[$ext])) { return $this->allowedMaxFileSize[$ext]; } $category = File::get_app_category($ext); if ($category && isset($this->allowedMaxFileSize['[' . $category . ']'])) { return $this->allowedMaxFileSize['[' . $category . ']']; } } return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false; } /** * Set filesize maximums (in bytes or INI format). * Automatically converts extensions to lowercase * for easier matching. * * Example: * <code> * array('*' => 200, 'jpg' => 1000, '[doc]' => '5m') * </code> * * @param array|int|string $rules */ public function setAllowedMaxFileSize($rules) { if (is_array($rules) && count($rules)) { // make sure all extensions are lowercase $rules = array_change_key_case($rules, CASE_LOWER); $finalRules = []; foreach ($rules as $rule => $value) { if (is_numeric($value)) { $tmpSize = $value; } else { $tmpSize = File::ini2bytes($value); } $finalRules[$rule] = (int)$tmpSize; } $this->allowedMaxFileSize = $finalRules; } elseif (is_string($rules)) { $this->allowedMaxFileSize['*'] = File::ini2bytes($rules); } elseif ((int)$rules > 0) { $this->allowedMaxFileSize['*'] = (int)$rules; } } /** * @return array */ public function getAllowedExtensions() { return $this->allowedExtensions; } /** * Limit allowed file extensions. Empty by default, allowing all extensions. * To allow files without an extension, use an empty string. * See {@link File::$allowed_extensions} to get a good standard set of * extensions that are typically not harmful in a webserver context. * See {@link setAllowedMaxFileSize()} to limit file size by extension. * * @param array $rules List of extensions */ public function setAllowedExtensions($rules) { if (!is_array($rules)) { return; } // make sure all rules are lowercase foreach ($rules as &$rule) { $rule = strtolower($rule); } $this->allowedExtensions = $rules; } /** * Determines if the bytesize of an uploaded * file is valid - can be defined on an * extension-by-extension basis in {@link $allowedMaxFileSize} * * @return boolean */ public function isValidSize() { // If file was blocked via PHP for being excessive size, shortcut here switch ($this->tmpFile['error']) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: return false; } $maxSize = $this->getAllowedMaxFileSize($this->getFileExtension()); return (!$this->tmpFile['size'] || !$maxSize || (int)$this->tmpFile['size'] < $maxSize); } /** * Determine if this file is valid but empty * * @return bool */ public function isFileEmpty() { // Don't check file size for errors if ($this->tmpFile['error'] !== UPLOAD_ERR_OK) { return false; } return empty($this->tmpFile['size']); } /** * Determines if the temporary file has a valid extension * An empty string in the validation map indicates files without an extension. * @return boolean */ public function isValidExtension() { return !count($this->allowedExtensions) || in_array($this->getFileExtension(), $this->allowedExtensions, true); } /** * Return the extension of the uploaded file, in lowercase * Returns an empty string for files without an extension * * @return string */ public function getFileExtension() { $pathInfo = pathinfo($this->tmpFile['name']); if (isset($pathInfo['extension'])) { return strtolower($pathInfo['extension']); } // Special case for files without extensions return ''; } /** * Run through the rules for this validator checking against * the temporary file set by {@link setTmpFile()} to see if * the file is deemed valid or not. * * @return boolean */ public function validate() { // we don't validate for empty upload fields yet if (empty($this->tmpFile['name'])) { return true; } // Check file upload if (!$this->isValidUpload()) { $this->errors[] = _t('SilverStripe\\Assets\\File.NOVALIDUPLOAD', 'File is not a valid upload'); return false; } if (!$this->isCompleteUpload()) { $this->errors[] = _t( 'SilverStripe\\Assets\\File.PARTIALUPLOAD', 'File did not finish uploading, please try again' ); return false; } // Check file isn't empty if ($this->isFileEmpty()) { $this->errors[] = _t('SilverStripe\\Assets\\File.NOFILESIZE', 'Filesize is zero bytes.'); return false; } // filesize validation if (!$this->isValidSize()) { $arg = File::format_size($this->getAllowedMaxFileSize($this->getFileExtension())); $this->errors[] = _t( 'SilverStripe\\Assets\\File.TOOLARGE', 'Filesize is too large, maximum {size} allowed', 'Argument 1: Filesize (e.g. 1MB)', ['size' => $arg] ); return false; } // extension validation if (!$this->isValidExtension()) { $this->errors[] = _t( 'SilverStripe\\Assets\\File.INVALIDEXTENSION_SHORT_EXT', 'Extension \'{extension}\' is not allowed', [ 'extension' => $this->getFileExtension() ] ); return false; } return true; } /** * Check that a valid file was given for upload (ignores file size) * * @return bool */ public function isValidUpload() { // Check file upload if (in_array($this->tmpFile['error'], [UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE])) { return false; } // Note that some "max file size" errors leave "tmp_name" empty, so don't fail on this. if (empty($this->tmpFile['tmp_name'])) { return true; } // Check if file is valid uploaded (with exception for unit testing) $useUploadedFile = $this->config()->get('use_is_uploaded_file'); if ($useUploadedFile && !is_uploaded_file($this->tmpFile['tmp_name'])) { return false; } return true; } /** * Check whether the file was fully uploaded * * @return bool */ public function isCompleteUpload() { return ($this->tmpFile['error'] !== UPLOAD_ERR_PARTIAL); } } |