You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
8.5KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\web;
  8. use yii\base\Object;
  9. use yii\helpers\Html;
  10. /**
  11. * UploadedFile represents the information for an uploaded file.
  12. *
  13. * You can call [[getInstance()]] to retrieve the instance of an uploaded file,
  14. * and then use [[saveAs()]] to save it on the server.
  15. * You may also query other information about the file, including [[name]],
  16. * [[tempName]], [[type]], [[size]] and [[error]].
  17. *
  18. * @property string $baseName Original file base name. This property is read-only.
  19. * @property string $extension File extension. This property is read-only.
  20. * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed
  21. * error code information. This property is read-only.
  22. *
  23. * @author Qiang Xue <qiang.xue@gmail.com>
  24. * @since 2.0
  25. */
  26. class UploadedFile extends Object
  27. {
  28. /**
  29. * @var string the original name of the file being uploaded
  30. */
  31. public $name;
  32. /**
  33. * @var string the path of the uploaded file on the server.
  34. * Note, this is a temporary file which will be automatically deleted by PHP
  35. * after the current request is processed.
  36. */
  37. public $tempName;
  38. /**
  39. * @var string the MIME-type of the uploaded file (such as "image/gif").
  40. * Since this MIME type is not checked on the server-side, do not take this value for granted.
  41. * Instead, use [[\yii\helpers\FileHelper::getMimeType()]] to determine the exact MIME type.
  42. */
  43. public $type;
  44. /**
  45. * @var integer the actual size of the uploaded file in bytes
  46. */
  47. public $size;
  48. /**
  49. * @var integer an error code describing the status of this file uploading.
  50. * @see http://www.php.net/manual/en/features.file-upload.errors.php
  51. */
  52. public $error;
  53. private static $_files;
  54. /**
  55. * String output.
  56. * This is PHP magic method that returns string representation of an object.
  57. * The implementation here returns the uploaded file's name.
  58. * @return string the string representation of the object
  59. */
  60. public function __toString()
  61. {
  62. return $this->name;
  63. }
  64. /**
  65. * Returns an uploaded file for the given model attribute.
  66. * The file should be uploaded using [[\yii\widgets\ActiveField::fileInput()]].
  67. * @param \yii\base\Model $model the data model
  68. * @param string $attribute the attribute name. The attribute name may contain array indexes.
  69. * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array.
  70. * @return UploadedFile the instance of the uploaded file.
  71. * Null is returned if no file is uploaded for the specified model attribute.
  72. * @see getInstanceByName()
  73. */
  74. public static function getInstance($model, $attribute)
  75. {
  76. $name = Html::getInputName($model, $attribute);
  77. return static::getInstanceByName($name);
  78. }
  79. /**
  80. * Returns all uploaded files for the given model attribute.
  81. * @param \yii\base\Model $model the data model
  82. * @param string $attribute the attribute name. The attribute name may contain array indexes
  83. * for tabular file uploading, e.g. '[1]file'.
  84. * @return UploadedFile[] array of UploadedFile objects.
  85. * Empty array is returned if no available file was found for the given attribute.
  86. */
  87. public static function getInstances($model, $attribute)
  88. {
  89. $name = Html::getInputName($model, $attribute);
  90. return static::getInstancesByName($name);
  91. }
  92. /**
  93. * Returns an uploaded file according to the given file input name.
  94. * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]').
  95. * @param string $name the name of the file input field.
  96. * @return null|UploadedFile the instance of the uploaded file.
  97. * Null is returned if no file is uploaded for the specified name.
  98. */
  99. public static function getInstanceByName($name)
  100. {
  101. $files = self::loadFiles();
  102. return isset($files[$name]) ? new static($files[$name]) : null;
  103. }
  104. /**
  105. * Returns an array of uploaded files corresponding to the specified file input name.
  106. * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]',
  107. * 'files[n]'..., and you can retrieve them all by passing 'files' as the name.
  108. * @param string $name the name of the array of files
  109. * @return UploadedFile[] the array of UploadedFile objects. Empty array is returned
  110. * if no adequate upload was found. Please note that this array will contain
  111. * all files from all sub-arrays regardless how deeply nested they are.
  112. */
  113. public static function getInstancesByName($name)
  114. {
  115. $files = self::loadFiles();
  116. if (isset($files[$name])) {
  117. return [new static($files[$name])];
  118. }
  119. $results = [];
  120. foreach ($files as $key => $file) {
  121. if (strpos($key, "{$name}[") === 0) {
  122. $results[] = new static($file);
  123. }
  124. }
  125. return $results;
  126. }
  127. /**
  128. * Cleans up the loaded UploadedFile instances.
  129. * This method is mainly used by test scripts to set up a fixture.
  130. */
  131. public static function reset()
  132. {
  133. self::$_files = null;
  134. }
  135. /**
  136. * Saves the uploaded file.
  137. * Note that this method uses php's move_uploaded_file() method. If the target file `$file`
  138. * already exists, it will be overwritten.
  139. * @param string $file the file path used to save the uploaded file
  140. * @param boolean $deleteTempFile whether to delete the temporary file after saving.
  141. * If true, you will not be able to save the uploaded file again in the current request.
  142. * @return boolean true whether the file is saved successfully
  143. * @see error
  144. */
  145. public function saveAs($file, $deleteTempFile = true)
  146. {
  147. if ($this->error == UPLOAD_ERR_OK) {
  148. if ($deleteTempFile) {
  149. return move_uploaded_file($this->tempName, $file);
  150. } elseif (is_uploaded_file($this->tempName)) {
  151. return copy($this->tempName, $file);
  152. }
  153. }
  154. return false;
  155. }
  156. /**
  157. * @return string original file base name
  158. */
  159. public function getBaseName()
  160. {
  161. // https://github.com/yiisoft/yii2/issues/11012
  162. $pathInfo = pathinfo('_' . $this->name, PATHINFO_FILENAME);
  163. return mb_substr($pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit');
  164. }
  165. /**
  166. * @return string file extension
  167. */
  168. public function getExtension()
  169. {
  170. return strtolower(pathinfo($this->name, PATHINFO_EXTENSION));
  171. }
  172. /**
  173. * @return boolean whether there is an error with the uploaded file.
  174. * Check [[error]] for detailed error code information.
  175. */
  176. public function getHasError()
  177. {
  178. return $this->error != UPLOAD_ERR_OK;
  179. }
  180. /**
  181. * Creates UploadedFile instances from $_FILE.
  182. * @return array the UploadedFile instances
  183. */
  184. private static function loadFiles()
  185. {
  186. if (self::$_files === null) {
  187. self::$_files = [];
  188. if (isset($_FILES) && is_array($_FILES)) {
  189. foreach ($_FILES as $class => $info) {
  190. self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
  191. }
  192. }
  193. }
  194. return self::$_files;
  195. }
  196. /**
  197. * Creates UploadedFile instances from $_FILE recursively.
  198. * @param string $key key for identifying uploaded file: class name and sub-array indexes
  199. * @param mixed $names file names provided by PHP
  200. * @param mixed $tempNames temporary file names provided by PHP
  201. * @param mixed $types file types provided by PHP
  202. * @param mixed $sizes file sizes provided by PHP
  203. * @param mixed $errors uploading issues provided by PHP
  204. */
  205. private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors)
  206. {
  207. if (is_array($names)) {
  208. foreach ($names as $i => $name) {
  209. self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
  210. }
  211. } elseif ((int)$errors !== UPLOAD_ERR_NO_FILE) {
  212. self::$_files[$key] = [
  213. 'name' => $names,
  214. 'tempName' => $tempNames,
  215. 'type' => $types,
  216. 'size' => $sizes,
  217. 'error' => $errors,
  218. ];
  219. }
  220. }
  221. }