1 | <?php |
||||||
2 | /** |
||||||
3 | * @link https://www.yiiframework.com/ |
||||||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||||||
5 | * @license https://www.yiiframework.com/license/ |
||||||
6 | */ |
||||||
7 | |||||||
8 | namespace yii\web; |
||||||
9 | |||||||
10 | use Yii; |
||||||
11 | use yii\base\BaseObject; |
||||||
12 | use yii\helpers\ArrayHelper; |
||||||
13 | use yii\helpers\Html; |
||||||
14 | |||||||
15 | /** |
||||||
16 | * UploadedFile represents the information for an uploaded file. |
||||||
17 | * |
||||||
18 | * You can call [[getInstance()]] to retrieve the instance of an uploaded file, |
||||||
19 | * and then use [[saveAs()]] to save it on the server. |
||||||
20 | * You may also query other information about the file, including [[name]], |
||||||
21 | * [[tempName]], [[type]], [[size]], [[error]] and [[fullPath]]. |
||||||
22 | * |
||||||
23 | * For more details and usage information on UploadedFile, see the [guide article on handling uploads](guide:input-file-upload). |
||||||
24 | * |
||||||
25 | * @property-read string $baseName Original file base name. |
||||||
26 | * @property-read string $extension File extension. |
||||||
27 | * @property-read bool $hasError Whether there is an error with the uploaded file. Check [[error]] for |
||||||
28 | * detailed error code information. |
||||||
29 | * |
||||||
30 | * @author Qiang Xue <[email protected]> |
||||||
31 | * @since 2.0 |
||||||
32 | */ |
||||||
33 | class UploadedFile extends BaseObject |
||||||
34 | { |
||||||
35 | /** |
||||||
36 | * @var string the original name of the file being uploaded |
||||||
37 | */ |
||||||
38 | public $name; |
||||||
39 | /** |
||||||
40 | * @var string the path of the uploaded file on the server. |
||||||
41 | * Note, this is a temporary file which will be automatically deleted by PHP |
||||||
42 | * after the current request is processed. |
||||||
43 | */ |
||||||
44 | public $tempName; |
||||||
45 | /** |
||||||
46 | * @var string the MIME-type of the uploaded file (such as "image/gif"). |
||||||
47 | * Since this MIME type is not checked on the server-side, do not take this value for granted. |
||||||
48 | * Instead, use [[\yii\helpers\FileHelper::getMimeType()]] to determine the exact MIME type. |
||||||
49 | */ |
||||||
50 | public $type; |
||||||
51 | /** |
||||||
52 | * @var int the actual size of the uploaded file in bytes |
||||||
53 | */ |
||||||
54 | public $size; |
||||||
55 | /** |
||||||
56 | * @var int an error code describing the status of this file uploading. |
||||||
57 | * @see https://www.php.net/manual/en/features.file-upload.errors.php |
||||||
58 | */ |
||||||
59 | public $error; |
||||||
60 | /** |
||||||
61 | * @var string|null The full path as submitted by the browser. Note this value does not always |
||||||
62 | * contain a real directory structure, and cannot be trusted. Available as of PHP 8.1. |
||||||
63 | * @since 2.0.46 |
||||||
64 | */ |
||||||
65 | public $fullPath; |
||||||
66 | |||||||
67 | /** |
||||||
68 | * @var resource|null a temporary uploaded stream resource used within PUT and PATCH request. |
||||||
69 | */ |
||||||
70 | private $_tempResource; |
||||||
71 | /** |
||||||
72 | * @var array[] |
||||||
73 | */ |
||||||
74 | private static $_files; |
||||||
75 | |||||||
76 | |||||||
77 | /** |
||||||
78 | * UploadedFile constructor. |
||||||
79 | * |
||||||
80 | * @param array $config name-value pairs that will be used to initialize the object properties |
||||||
81 | */ |
||||||
82 | 53 | public function __construct($config = []) |
|||||
83 | { |
||||||
84 | 53 | $this->_tempResource = ArrayHelper::remove($config, 'tempResource'); |
|||||
85 | 53 | parent::__construct($config); |
|||||
86 | } |
||||||
87 | |||||||
88 | /** |
||||||
89 | * String output. |
||||||
90 | * This is PHP magic method that returns string representation of an object. |
||||||
91 | * The implementation here returns the uploaded file's name. |
||||||
92 | * @return string the string representation of the object |
||||||
93 | */ |
||||||
94 | public function __toString() |
||||||
95 | { |
||||||
96 | return $this->name; |
||||||
97 | } |
||||||
98 | |||||||
99 | /** |
||||||
100 | * Returns an uploaded file for the given model attribute. |
||||||
101 | * The file should be uploaded using [[\yii\widgets\ActiveField::fileInput()]]. |
||||||
102 | * @param \yii\base\Model $model the data model |
||||||
103 | * @param string $attribute the attribute name. The attribute name may contain array indexes. |
||||||
104 | * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array. |
||||||
105 | * @return UploadedFile|null the instance of the uploaded file. |
||||||
106 | * Null is returned if no file is uploaded for the specified model attribute. |
||||||
107 | * @see getInstanceByName() |
||||||
108 | */ |
||||||
109 | 2 | public static function getInstance($model, $attribute) |
|||||
110 | { |
||||||
111 | 2 | $name = Html::getInputName($model, $attribute); |
|||||
112 | 2 | return static::getInstanceByName($name); |
|||||
113 | } |
||||||
114 | |||||||
115 | /** |
||||||
116 | * Returns all uploaded files for the given model attribute. |
||||||
117 | * @param \yii\base\Model $model the data model |
||||||
118 | * @param string $attribute the attribute name. The attribute name may contain array indexes |
||||||
119 | * for tabular file uploading, e.g. '[1]file'. |
||||||
120 | * @return UploadedFile[] array of UploadedFile objects. |
||||||
121 | * Empty array is returned if no available file was found for the given attribute. |
||||||
122 | */ |
||||||
123 | 1 | public static function getInstances($model, $attribute) |
|||||
124 | { |
||||||
125 | 1 | $name = Html::getInputName($model, $attribute); |
|||||
126 | 1 | return static::getInstancesByName($name); |
|||||
127 | } |
||||||
128 | |||||||
129 | /** |
||||||
130 | * Returns an uploaded file according to the given file input name. |
||||||
131 | * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]'). |
||||||
132 | * @param string $name the name of the file input field. |
||||||
133 | * @return UploadedFile|null the instance of the uploaded file. |
||||||
134 | * Null is returned if no file is uploaded for the specified name. |
||||||
135 | */ |
||||||
136 | 2 | public static function getInstanceByName($name) |
|||||
137 | { |
||||||
138 | 2 | $files = self::loadFiles(); |
|||||
139 | 2 | return isset($files[$name]) ? new static($files[$name]) : null; |
|||||
140 | } |
||||||
141 | |||||||
142 | /** |
||||||
143 | * Returns an array of uploaded files corresponding to the specified file input name. |
||||||
144 | * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]', |
||||||
145 | * 'files[n]'..., and you can retrieve them all by passing 'files' as the name. |
||||||
146 | * @param string $name the name of the array of files |
||||||
147 | * @return UploadedFile[] the array of UploadedFile objects. Empty array is returned |
||||||
148 | * if no adequate upload was found. Please note that this array will contain |
||||||
149 | * all files from all sub-arrays regardless how deeply nested they are. |
||||||
150 | */ |
||||||
151 | 1 | public static function getInstancesByName($name) |
|||||
152 | { |
||||||
153 | 1 | $files = self::loadFiles(); |
|||||
154 | 1 | if (isset($files[$name])) { |
|||||
155 | return [new static($files[$name])]; |
||||||
156 | } |
||||||
157 | 1 | $results = []; |
|||||
158 | 1 | foreach ($files as $key => $file) { |
|||||
159 | 1 | if (strpos($key, "{$name}[") === 0) { |
|||||
160 | 1 | $results[] = new static($file); |
|||||
161 | } |
||||||
162 | } |
||||||
163 | |||||||
164 | 1 | return $results; |
|||||
165 | } |
||||||
166 | |||||||
167 | /** |
||||||
168 | * Cleans up the loaded UploadedFile instances. |
||||||
169 | * This method is mainly used by test scripts to set up a fixture. |
||||||
170 | */ |
||||||
171 | 1 | public static function reset() |
|||||
172 | { |
||||||
173 | 1 | self::$_files = null; |
|||||
174 | } |
||||||
175 | |||||||
176 | /** |
||||||
177 | * Saves the uploaded file. |
||||||
178 | * If the target file `$file` already exists, it will be overwritten. |
||||||
179 | * @param string $file the file path or a path alias used to save the uploaded file. |
||||||
180 | * @param bool $deleteTempFile whether to delete the temporary file after saving. |
||||||
181 | * If true, you will not be able to save the uploaded file again in the current request. |
||||||
182 | * @return bool true whether the file is saved successfully |
||||||
183 | * @see error |
||||||
184 | */ |
||||||
185 | 1 | public function saveAs($file, $deleteTempFile = true) |
|||||
186 | { |
||||||
187 | 1 | if ($this->hasError) { |
|||||
188 | return false; |
||||||
189 | } |
||||||
190 | |||||||
191 | 1 | $targetFile = Yii::getAlias($file); |
|||||
192 | 1 | if (is_resource($this->_tempResource)) { |
|||||
193 | 1 | $result = $this->copyTempFile($targetFile); |
|||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||||
194 | 1 | return $deleteTempFile ? @fclose($this->_tempResource) : (bool) $result; |
|||||
195 | } |
||||||
196 | |||||||
197 | 1 | return $deleteTempFile ? move_uploaded_file($this->tempName, $targetFile) : copy($this->tempName, $targetFile); |
|||||
0 ignored issues
–
show
It seems like
$targetFile can also be of type false ; however, parameter $to of copy() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
$targetFile can also be of type false ; however, parameter $to of move_uploaded_file() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
198 | } |
||||||
199 | |||||||
200 | /** |
||||||
201 | * Copy temporary file into file specified |
||||||
202 | * |
||||||
203 | * @param string $targetFile path of the file to copy to |
||||||
204 | * @return int|false the total count of bytes copied, or false on failure |
||||||
205 | * @since 2.0.32 |
||||||
206 | */ |
||||||
207 | 1 | protected function copyTempFile($targetFile) |
|||||
208 | { |
||||||
209 | 1 | $target = fopen($targetFile, 'wb'); |
|||||
210 | 1 | if ($target === false) { |
|||||
211 | return false; |
||||||
212 | } |
||||||
213 | |||||||
214 | 1 | $result = stream_copy_to_stream($this->_tempResource, $target); |
|||||
215 | 1 | @fclose($target); |
|||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
fclose() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||||||
216 | |||||||
217 | 1 | return $result; |
|||||
218 | } |
||||||
219 | |||||||
220 | /** |
||||||
221 | * @return string original file base name |
||||||
222 | */ |
||||||
223 | 1 | public function getBaseName() |
|||||
224 | { |
||||||
225 | // https://github.com/yiisoft/yii2/issues/11012 |
||||||
226 | 1 | $pathInfo = pathinfo('_' . $this->name, PATHINFO_FILENAME); |
|||||
227 | 1 | return mb_substr($pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit'); |
|||||
228 | } |
||||||
229 | |||||||
230 | /** |
||||||
231 | * @return string file extension |
||||||
232 | */ |
||||||
233 | 17 | public function getExtension() |
|||||
234 | { |
||||||
235 | 17 | return strtolower(pathinfo($this->name, PATHINFO_EXTENSION)); |
|||||
236 | } |
||||||
237 | |||||||
238 | /** |
||||||
239 | * @return bool whether there is an error with the uploaded file. |
||||||
240 | * Check [[error]] for detailed error code information. |
||||||
241 | */ |
||||||
242 | 1 | public function getHasError() |
|||||
243 | { |
||||||
244 | 1 | return $this->error != UPLOAD_ERR_OK; |
|||||
245 | } |
||||||
246 | |||||||
247 | /** |
||||||
248 | * Returns reformated data of uplodaded files. |
||||||
249 | * |
||||||
250 | * @return array[] |
||||||
251 | */ |
||||||
252 | 3 | private static function loadFiles() |
|||||
253 | { |
||||||
254 | 3 | if (self::$_files === null) { |
|||||
255 | 2 | self::$_files = []; |
|||||
256 | 2 | if (isset($_FILES) && is_array($_FILES)) { |
|||||
257 | 2 | foreach ($_FILES as $key => $info) { |
|||||
258 | 2 | self::loadFilesRecursive( |
|||||
259 | 2 | $key, |
|||||
260 | 2 | $info['name'], |
|||||
261 | 2 | $info['tmp_name'], |
|||||
262 | 2 | $info['type'], |
|||||
263 | 2 | $info['size'], |
|||||
264 | 2 | $info['error'], |
|||||
265 | 2 | isset($info['full_path']) ? $info['full_path'] : [], |
|||||
266 | 2 | isset($info['tmp_resource']) ? $info['tmp_resource'] : [] |
|||||
267 | 2 | ); |
|||||
268 | } |
||||||
269 | } |
||||||
270 | } |
||||||
271 | |||||||
272 | 3 | return self::$_files; |
|||||
273 | } |
||||||
274 | |||||||
275 | /** |
||||||
276 | * Recursive reformats data of uplodaded file(s). |
||||||
277 | * |
||||||
278 | * @param string $key key for identifying uploaded file(sub-array index) |
||||||
279 | * @param string[]|string $names file name(s) provided by PHP |
||||||
280 | * @param string[]|string $tempNames temporary file name(s) provided by PHP |
||||||
281 | * @param string[]|string $types file type(s) provided by PHP |
||||||
282 | * @param int[]|int $sizes file size(s) provided by PHP |
||||||
283 | * @param int[]|int $errors uploading issue(s) provided by PHP |
||||||
284 | * @param array|string|null $fullPaths the full path(s) as submitted by the browser/PHP |
||||||
285 | * @param array|resource|null $tempResources the resource(s) |
||||||
286 | */ |
||||||
287 | 2 | private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors, $fullPaths, $tempResources) |
|||||
288 | { |
||||||
289 | 2 | if (is_array($names)) { |
|||||
290 | 1 | foreach ($names as $i => $name) { |
|||||
291 | 1 | self::loadFilesRecursive( |
|||||
292 | 1 | $key . '[' . $i . ']', |
|||||
293 | 1 | $name, |
|||||
294 | 1 | $tempNames[$i], |
|||||
295 | 1 | $types[$i], |
|||||
296 | 1 | $sizes[$i], |
|||||
297 | 1 | $errors[$i], |
|||||
298 | 1 | isset($fullPaths[$i]) ? $fullPaths[$i] : null, |
|||||
299 | 1 | isset($tempResources[$i]) ? $tempResources[$i] : null |
|||||
300 | 1 | ); |
|||||
301 | } |
||||||
302 | 2 | } elseif ($errors != UPLOAD_ERR_NO_FILE) { |
|||||
303 | 2 | self::$_files[$key] = [ |
|||||
304 | 2 | 'name' => $names, |
|||||
305 | 2 | 'tempName' => $tempNames, |
|||||
306 | 2 | 'tempResource' => is_resource($tempResources) ? $tempResources : null, |
|||||
307 | 2 | 'type' => $types, |
|||||
308 | 2 | 'size' => $sizes, |
|||||
309 | 2 | 'error' => $errors, |
|||||
310 | 2 | 'fullPath' => is_string($fullPaths) ? $fullPaths : null, |
|||||
311 | 2 | ]; |
|||||
312 | } |
||||||
313 | } |
||||||
314 | } |
||||||
315 |