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

UploadedFile::getBaseName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.125

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 5
ccs 2
cts 4
cp 0.5
crap 1.125
rs 10
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