Passed
Push — master ( f6d328...4cfdfc )
by Paweł
23:43
created

UploadedFile::loadFilesRecursive()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 24
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

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

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 http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://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
    /**
73 47
     * @var array[]
74
     */
75 47
    private static $_files;
76 47
77 47
78
    /**
79
     * UploadedFile constructor.
80
     *
81
     * @param array $config name-value pairs that will be used to initialize the object properties
82
     */
83
    public function __construct($config = [])
84
    {
85
        $this->_tempResource = ArrayHelper::remove($config, 'tempResource');
86
        parent::__construct($config);
87
    }
88
89
    /**
90
     * String output.
91
     * This is PHP magic method that returns string representation of an object.
92
     * The implementation here returns the uploaded file's name.
93
     * @return string the string representation of the object
94
     */
95
    public function __toString()
96
    {
97
        return $this->name;
98
    }
99
100 2
    /**
101
     * Returns an uploaded file for the given model attribute.
102 2
     * The file should be uploaded using [[\yii\widgets\ActiveField::fileInput()]].
103 2
     * @param \yii\base\Model $model the data model
104
     * @param string $attribute the attribute name. The attribute name may contain array indexes.
105
     * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array.
106
     * @return null|UploadedFile the instance of the uploaded file.
107
     * Null is returned if no file is uploaded for the specified model attribute.
108
     * @see getInstanceByName()
109
     */
110
    public static function getInstance($model, $attribute)
111
    {
112
        $name = Html::getInputName($model, $attribute);
113
        return static::getInstanceByName($name);
114 1
    }
115
116 1
    /**
117 1
     * Returns all uploaded files for the given model attribute.
118
     * @param \yii\base\Model $model the data model
119
     * @param string $attribute the attribute name. The attribute name may contain array indexes
120
     * for tabular file uploading, e.g. '[1]file'.
121
     * @return UploadedFile[] array of UploadedFile objects.
122
     * Empty array is returned if no available file was found for the given attribute.
123
     */
124
    public static function getInstances($model, $attribute)
125
    {
126
        $name = Html::getInputName($model, $attribute);
127 2
        return static::getInstancesByName($name);
128
    }
129 2
130 2
    /**
131
     * Returns an uploaded file according to the given file input name.
132
     * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]').
133
     * @param string $name the name of the file input field.
134
     * @return null|UploadedFile the instance of the uploaded file.
135
     * Null is returned if no file is uploaded for the specified name.
136
     */
137
    public static function getInstanceByName($name)
138
    {
139
        $files = self::loadFiles();
140
        return isset($files[$name]) ? new static($files[$name]) : null;
141
    }
142 1
143
    /**
144 1
     * Returns an array of uploaded files corresponding to the specified file input name.
145 1
     * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]',
146
     * 'files[n]'..., and you can retrieve them all by passing 'files' as the name.
147
     * @param string $name the name of the array of files
148 1
     * @return UploadedFile[] the array of UploadedFile objects. Empty array is returned
149 1
     * if no adequate upload was found. Please note that this array will contain
150 1
     * all files from all sub-arrays regardless how deeply nested they are.
151 1
     */
152
    public static function getInstancesByName($name)
153
    {
154
        $files = self::loadFiles();
155 1
        if (isset($files[$name])) {
156
            return [new static($files[$name])];
157
        }
158
        $results = [];
159
        foreach ($files as $key => $file) {
160
            if (strpos($key, "{$name}[") === 0) {
161
                $results[] = new static($file);
162 1
            }
163
        }
164 1
165 1
        return $results;
166
    }
167
168
    /**
169
     * Cleans up the loaded UploadedFile instances.
170
     * This method is mainly used by test scripts to set up a fixture.
171
     */
172
    public static function reset()
173
    {
174
        self::$_files = null;
175
    }
176 1
177
    /**
178 1
     * Saves the uploaded file.
179
     * If the target file `$file` already exists, it will be overwritten.
180
     * @param string $file the file path or a path alias used to save the uploaded file.
181
     * @param bool $deleteTempFile whether to delete the temporary file after saving.
182 1
     * If true, you will not be able to save the uploaded file again in the current request.
183 1
     * @return bool true whether the file is saved successfully
184 1
     * @see error
185 1
     */
186
    public function saveAs($file, $deleteTempFile = true)
187
    {
188 1
        if ($this->hasError) {
189
            return false;
190
        }
191
192
        $targetFile = Yii::getAlias($file);
193
        if (is_resource($this->_tempResource)) {
194
            $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

194
            $result = $this->copyTempFile(/** @scrutinizer ignore-type */ $targetFile);
Loading history...
195
            return $deleteTempFile ? @fclose($this->_tempResource) : (bool) $result;
196
        }
197
198 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 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

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

198
        return $deleteTempFile ? move_uploaded_file($this->tempName, $targetFile) : copy($this->tempName, /** @scrutinizer ignore-type */ $targetFile);
Loading history...
199
    }
200 1
201 1
    /**
202
     * Copy temporary file into file specified
203
     *
204
     * @param string $targetFile path of the file to copy to
205 1
     * @return int|false the total count of bytes copied, or false on failure
206 1
     * @since 2.0.32
207
     */
208 1
    protected function copyTempFile($targetFile)
209
    {
210
        $target = fopen($targetFile, 'wb');
211
        if ($target === false) {
212
            return false;
213
        }
214 1
215
        $result = stream_copy_to_stream($this->_tempResource, $target);
216
        @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

216
        /** @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...
217 1
218 1
        return $result;
219
    }
220
221
    /**
222
     * @return string original file base name
223
     */
224 17
    public function getBaseName()
225
    {
226 17
        // https://github.com/yiisoft/yii2/issues/11012
227
        $pathInfo = pathinfo('_' . $this->name, PATHINFO_FILENAME);
228
        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_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

228
        return mb_substr(/** @scrutinizer ignore-type */ $pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit');
Loading history...
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

228
        return mb_substr($pathInfo, 1, mb_strlen(/** @scrutinizer ignore-type */ $pathInfo, '8bit'), '8bit');
Loading history...
229
    }
230
231
    /**
232
     * @return string file extension
233 1
     */
234
    public function getExtension()
235 1
    {
236
        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

236
        return strtolower(/** @scrutinizer ignore-type */ pathinfo($this->name, PATHINFO_EXTENSION));
Loading history...
237
    }
238
239
    /**
240
     * @return bool whether there is an error with the uploaded file.
241
     * Check [[error]] for detailed error code information.
242 3
     */
243
    public function getHasError()
244 3
    {
245 2
        return $this->error != UPLOAD_ERR_OK;
246 2
    }
247 2
248 2
    /**
249 2
     * Returns reformated data of uplodaded files.
250
     *
251
     * @return array[]
252
     */
253
    private static function loadFiles()
254 3
    {
255
        if (self::$_files === null) {
0 ignored issues
show
introduced by
The condition self::_files === null is always false.
Loading history...
256
            self::$_files = [];
257
            if (isset($_FILES) && is_array($_FILES)) {
258
                foreach ($_FILES as $key => $info) {
259
                    self::loadFilesRecursive(
260
                        $key,
261
                        $info['name'],
262
                        $info['tmp_name'],
263
                        $info['type'],
264
                        $info['size'],
265
                        $info['error'],
266 2
                        isset($info['full_path']) ? $info['full_path'] : [],
267
                        isset($info['tmp_resource']) ? $info['tmp_resource'] : []
268 2
                    );
269 1
                }
270 1
            }
271 1
        }
272
273 2
        return self::$_files;
274 2
    }
275 2
276 2
    /**
277 2
     * Recursive reformats data of uplodaded file(s).
278 2
     *
279 2
     * @param string $key key for identifying uploaded file(sub-array index)
280 2
     * @param string[]|string $names file name(s) provided by PHP
281
     * @param string[]|string $tempNames temporary file name(s) provided by PHP
282
     * @param string[]|string $types file type(s) provided by PHP
283 2
     * @param int[]|int $sizes file size(s) provided by PHP
284
     * @param int[]|int $errors uploading issue(s) provided by PHP
285
     * @param array|string|null $fullPaths the full path(s) as submitted by the browser/PHP
286
     * @param array|resource|null $tempResources the resource(s)
287
     */
288
    private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors, $fullPaths, $tempResources)
289
    {
290
        if (is_array($names)) {
291
            foreach ($names as $i => $name) {
292
                self::loadFilesRecursive(
293
                    $key . '[' . $i . ']',
294
                    $name,
295
                    $tempNames[$i],
296
                    $types[$i],
297
                    $sizes[$i],
298
                    $errors[$i],
299
                    isset($fullPaths[$i]) ? $fullPaths[$i] : null,
300
                    isset($tempResources[$i]) ? $tempResources[$i] : null
301
                );
302
            }
303
        } elseif ($errors != UPLOAD_ERR_NO_FILE) {
304
            self::$_files[$key] = [
305
                'name' => $names,
306
                'tempName' => $tempNames,
307
                'tempResource' => is_resource($tempResources) ? $tempResources : null,
308
                'type' => $types,
309
                'size' => $sizes,
310
                'error' => $errors,
311
                'fullPath' => is_string($fullPaths) ? $fullPaths : null,
312
            ];
313
        }
314
    }
315
}
316