Completed
Push — remove-yii-autoloader ( 560d61...5f8600 )
by Alexander
27:04 queued 23:12
created

UploadedFile::setClientFilename()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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\http;
9
10
use Psr\Http\Message\StreamInterface;
11
use Psr\Http\Message\UploadedFileInterface;
12
use yii\base\BaseObject;
13
use yii\base\InvalidArgumentException;
14
use yii\di\Instance;
15
use yii\helpers\Html;
16
17
/**
18
 * UploadedFile represents the information for an uploaded file.
19
 *
20
 * You can call [[getInstance()]] to retrieve the instance of an uploaded file,
21
 * and then use [[saveAs()]] to save it on the server.
22
 * You may also query other information about the file, including [[clientFilename]],
23
 * [[tempFilename]], [[clientMediaType]], [[size]] and [[error]].
24
 *
25
 * For more details and usage information on UploadedFile, see the [guide article on handling uploads](guide:input-file-upload).
26
 *
27
 * @property string $clientFilename the original name of the file being uploaded.
28
 * @property int $error an error code describing the status of this file uploading.
29
 * @property int $size the actual size of the uploaded file in bytes.
30
 * @property string $clientMediaType  the MIME-type of the uploaded file (such as "image/gif").
31
 * Since this MIME type is not checked on the server-side, do not take this value for granted.
32
 * Instead, use [[\yii\helpers\FileHelper::getMimeType()]] to determine the exact MIME type.
33
 * @property string $baseName Original file base name. This property is read-only.
34
 * @property string $extension File extension. This property is read-only.
35
 * @property bool $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed
36
 * error code information. This property is read-only.
37
 *
38
 * @author Qiang Xue <[email protected]>
39
 * @author Paul Klimov <[email protected]>
40
 * @since 2.0
41
 */
42
class UploadedFile extends BaseObject implements UploadedFileInterface
43
{
44
    /**
45
     * @var string the path of the uploaded file on the server.
46
     * Note, this is a temporary file which will be automatically deleted by PHP
47
     * after the current request is processed.
48
     */
49
    public $tempFilename;
50
51
    /**
52
     * @var string the original name of the file being uploaded
53
     */
54
    private $_clientFilename;
55
    /**
56
     * @var string the MIME-type of the uploaded file (such as "image/gif").
57
     * Since this MIME type is not checked on the server-side, do not take this value for granted.
58
     * Instead, use [[\yii\helpers\FileHelper::getMimeType()]] to determine the exact MIME type.
59
     */
60
    private $_clientMediaType;
61
    /**
62
     * @var int the actual size of the uploaded file in bytes
63
     */
64
    private $_size;
65
    /**
66
     * @var int an error code describing the status of this file uploading.
67
     * @see http://www.php.net/manual/en/features.file-upload.errors.php
68
     */
69
    private $_error;
70
    /**
71
     * @var StreamInterface stream for this file.
72
     * @since 2.1.0
73
     */
74
    private $_stream;
75
76
    private static $_files;
77
78
79
    /**
80
     * String output.
81
     * This is PHP magic method that returns string representation of an object.
82
     * The implementation here returns the uploaded file's name.
83
     * @return string the string representation of the object
84
     */
85
    public function __toString()
86
    {
87
        return $this->clientFilename;
88
    }
89
90
    /**
91
     * Returns an uploaded file for the given model attribute.
92
     * The file should be uploaded using [[\yii\widgets\ActiveField::fileInput()]].
93
     * @param \yii\base\Model $model the data model
94
     * @param string $attribute the attribute name. The attribute name may contain array indexes.
95
     * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array.
96
     * @return UploadedFile the instance of the uploaded file.
97
     * Null is returned if no file is uploaded for the specified model attribute.
98
     * @see getInstanceByName()
99
     */
100
    public static function getInstance($model, $attribute)
101
    {
102
        $name = Html::getInputName($model, $attribute);
103
        return static::getInstanceByName($name);
104
    }
105
106
    /**
107
     * Returns all uploaded files for the given model attribute.
108
     * @param \yii\base\Model $model the data model
109
     * @param string $attribute the attribute name. The attribute name may contain array indexes
110
     * for tabular file uploading, e.g. '[1]file'.
111
     * @return UploadedFile[] array of UploadedFile objects.
112
     * Empty array is returned if no available file was found for the given attribute.
113
     */
114
    public static function getInstances($model, $attribute)
115
    {
116
        $name = Html::getInputName($model, $attribute);
117
        return static::getInstancesByName($name);
118
    }
119
120
    /**
121
     * Returns an uploaded file according to the given file input name.
122
     * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]').
123
     * @param string $name the name of the file input field.
124
     * @return null|UploadedFile the instance of the uploaded file.
125
     * Null is returned if no file is uploaded for the specified name.
126
     */
127
    public static function getInstanceByName($name)
128
    {
129
        $files = self::loadFiles();
130
        return isset($files[$name]) ? new static($files[$name]) : null;
131
    }
132
133
    /**
134
     * Returns an array of uploaded files corresponding to the specified file input name.
135
     * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]',
136
     * 'files[n]'..., and you can retrieve them all by passing 'files' as the name.
137
     * @param string $name the name of the array of files
138
     * @return UploadedFile[] the array of UploadedFile objects. Empty array is returned
139
     * if no adequate upload was found. Please note that this array will contain
140
     * all files from all sub-arrays regardless how deeply nested they are.
141
     */
142
    public static function getInstancesByName($name)
143
    {
144
        $files = self::loadFiles();
145
        if (isset($files[$name])) {
146
            return [new static($files[$name])];
147
        }
148
        $results = [];
149
        foreach ($files as $key => $file) {
150
            if (strpos($key, "{$name}[") === 0) {
151
                $results[] = new static($file);
152
            }
153
        }
154
        return $results;
155
    }
156
157
    /**
158
     * Cleans up the loaded UploadedFile instances.
159
     * This method is mainly used by test scripts to set up a fixture.
160
     */
161
    public static function reset()
162
    {
163
        self::$_files = null;
164
    }
165
166
    /**
167
     * Saves the uploaded file.
168
     * Note that this method uses php's move_uploaded_file() method. If the target file `$file`
169
     * already exists, it will be overwritten.
170
     * @param string $file the file path used to save the uploaded file
171
     * @param bool $deleteTempFile whether to delete the temporary file after saving.
172
     * If true, you will not be able to save the uploaded file again in the current request.
173
     * @return bool true whether the file is saved successfully
174
     * @see error
175
     */
176
    public function saveAs($file, $deleteTempFile = true)
177
    {
178
        if ($this->error == UPLOAD_ERR_OK) {
179
            if ($deleteTempFile) {
180
                $this->moveTo($file);
181
                return true;
182
            } elseif (is_uploaded_file($this->tempFilename)) {
183
                return copy($this->tempFilename, $file);
184
            }
185
        }
186
        return false;
187
    }
188
189
    /**
190
     * @return string original file base name
191
     */
192
    public function getBaseName()
193
    {
194
        // https://github.com/yiisoft/yii2/issues/11012
195
        $pathInfo = pathinfo('_' . $this->getClientFilename(), PATHINFO_FILENAME);
196
        return mb_substr($pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit');
197
    }
198
199
    /**
200
     * @return string file extension
201
     */
202
    public function getExtension()
203
    {
204
        return strtolower(pathinfo($this->getClientFilename(), PATHINFO_EXTENSION));
205
    }
206
207
    /**
208
     * @return bool whether there is an error with the uploaded file.
209
     * Check [[error]] for detailed error code information.
210
     */
211
    public function getHasError()
212
    {
213
        return $this->error != UPLOAD_ERR_OK;
214
    }
215
216
    /**
217
     * Creates UploadedFile instances from $_FILE.
218
     * @return array the UploadedFile instances
219
     */
220
    private static function loadFiles()
221
    {
222
        if (self::$_files === null) {
223
            self::$_files = [];
224
            if (isset($_FILES) && is_array($_FILES)) {
225
                foreach ($_FILES as $class => $info) {
226
                    self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
227
                }
228
            }
229
        }
230
        return self::$_files;
231
    }
232
233
    /**
234
     * Creates UploadedFile instances from $_FILE recursively.
235
     * @param string $key key for identifying uploaded file: class name and sub-array indexes
236
     * @param mixed $names file names provided by PHP
237
     * @param mixed $tempNames temporary file names provided by PHP
238
     * @param mixed $types file types provided by PHP
239
     * @param mixed $sizes file sizes provided by PHP
240
     * @param mixed $errors uploading issues provided by PHP
241
     */
242
    private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors)
243
    {
244
        if (is_array($names)) {
245
            foreach ($names as $i => $name) {
246
                self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
247
            }
248
        } elseif ((int) $errors !== UPLOAD_ERR_NO_FILE) {
249
            self::$_files[$key] = [
250
                'clientFilename' => $names,
251
                'tempFilename' => $tempNames,
252
                'clientMediaType' => $types,
253
                'size' => $sizes,
254
                'error' => $errors,
255
            ];
256
        }
257
    }
258
259
    /**
260
     * {@inheritdoc}
261
     * @since 2.1.0
262
     */
263
    public function getStream()
264
    {
265
        if (!$this->_stream instanceof StreamInterface) {
266
            if ($this->_stream === null) {
267
                if ($this->getError() !== UPLOAD_ERR_OK) {
268
                    throw new \RuntimeException('Unable to create file stream due to upload error: ' . $this->getError());
269
                }
270
                $stream = [
271
                    'class' => FileStream::class,
272
                    'filename' => $this->tempFilename,
273
                    'mode' => 'r',
274
                ];
275
            } elseif ($this->_stream instanceof \Closure) {
276
                $stream = call_user_func($this->_stream, $this);
277
            } else {
278
                $stream = $this->_stream;
279
            }
280
281
            $this->_stream = Instance::ensure($stream, StreamInterface::class);
282
        }
283
        return $this->_stream;
284
    }
285
286
    /**
287
     * @param StreamInterface|\Closure|array $stream stream instance or its DI compatible configuration.
288
     * @since 2.1.0
289
     */
290
    public function setStream($stream)
291
    {
292
        $this->_stream = $stream;
0 ignored issues
show
Documentation Bug introduced by
It seems like $stream can also be of type object<Closure> or array. However, the property $_stream is declared as type object<Psr\Http\Message\StreamInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     * @since 2.1.0
298
     */
299
    public function moveTo($targetPath)
300
    {
301
        if ($this->error !== UPLOAD_ERR_OK) {
302
            throw new \RuntimeException('Unable to move file due to upload error: ' . $this->error);
303
        }
304
        if (!move_uploaded_file($this->tempFilename, $targetPath)) {
305
            throw new \RuntimeException('Unable to move uploaded file.');
306
        }
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     * @since 2.1.0
312
     */
313
    public function getSize()
314
    {
315
        return $this->_size;
316
    }
317
318
    /**
319
     * @param int $size the actual size of the uploaded file in bytes.
320
     * @throws InvalidArgumentException on invalid size given.
321
     * @since 2.1.0
322
     */
323
    public function setSize($size)
324
    {
325
        if (!is_int($size)) {
326
            throw new InvalidArgumentException('"' . get_class($this) . '::$size" must be an integer.');
327
        }
328
        $this->_size = $size;
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     * @since 2.1.0
334
     */
335
    public function getError()
336
    {
337
        return $this->_error;
338
    }
339
340
    /**
341
     * @param int $error upload error code.
342
     * @throws InvalidArgumentException on invalid error given.
343
     * @since 2.1.0
344
     */
345
    public function setError($error)
346
    {
347
        if (!is_int($error)) {
348
            throw new InvalidArgumentException('"' . get_class($this) . '::$error" must be an integer.');
349
        }
350
        $this->_error = $error;
351
    }
352
353
    /**
354
     * {@inheritdoc}
355
     * @since 2.1.0
356
     */
357
    public function getClientFilename()
358
    {
359
        return $this->_clientFilename;
360
    }
361
362
    /**
363
     * @param string $clientFilename the original name of the file being uploaded.
364
     * @since 2.1.0
365
     */
366
    public function setClientFilename($clientFilename)
367
    {
368
        $this->_clientFilename = $clientFilename;
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     * @since 2.1.0
374
     */
375
    public function getClientMediaType()
376
    {
377
        return $this->_clientMediaType;
378
    }
379
380
    /**
381
     * @param string $clientMediaType the MIME-type of the uploaded file (such as "image/gif").
382
     * @since 2.1.0
383
     */
384
    public function setClientMediaType($clientMediaType)
385
    {
386
        $this->_clientMediaType = $clientMediaType;
387
    }
388
}
389