UploadedFile::loadFilesRecursive()   B
last analyzed

Complexity

Conditions 8
Paths 7

Size

Total Lines 24
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 20
nc 7
nop 8
dl 0
loc 24
ccs 22
cts 22
cp 1
crap 8
rs 8.4444
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
It seems like $targetFile can also be of type false; however, parameter $targetFile of yii\web\UploadedFile::copyTempFile() 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 ignore-type  annotation

193
            $result = $this->copyTempFile(/** @scrutinizer ignore-type */ $targetFile);
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
Bug introduced by
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 ignore-type  annotation

197
        return $deleteTempFile ? move_uploaded_file($this->tempName, $targetFile) : copy($this->tempName, /** @scrutinizer ignore-type */ $targetFile);
Loading history...
Bug introduced by
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 ignore-type  annotation

197
        return $deleteTempFile ? move_uploaded_file($this->tempName, /** @scrutinizer ignore-type */ $targetFile) : copy($this->tempName, $targetFile);
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
Security Best Practice introduced by
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 ignore-unhandled  annotation

215
        /** @scrutinizer ignore-unhandled */ @fclose($target);

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');
0 ignored issues
show
Bug introduced by
It seems like $pathInfo can also be of type array; however, parameter $string of mb_strlen() 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 ignore-type  annotation

227
        return mb_substr($pathInfo, 1, mb_strlen(/** @scrutinizer ignore-type */ $pathInfo, '8bit'), '8bit');
Loading history...
Bug introduced by
It seems like $pathInfo can also be of type array; however, parameter $string of mb_substr() 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 ignore-type  annotation

227
        return mb_substr(/** @scrutinizer ignore-type */ $pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit');
Loading history...
228
    }
229
230
    /**
231
     * @return string file extension
232
     */
233 17
    public function getExtension()
234
    {
235 17
        return strtolower(pathinfo($this->name, PATHINFO_EXTENSION));
0 ignored issues
show
Bug introduced by
It seems like pathinfo($this->name, yii\web\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() 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 ignore-type  annotation

235
        return strtolower(/** @scrutinizer ignore-type */ pathinfo($this->name, PATHINFO_EXTENSION));
Loading history...
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) {
0 ignored issues
show
introduced by
The condition self::_files === null is always false.
Loading history...
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