Completed
Push — master ( 4fd434...e1a522 )
by Bohuslav
03:35
created

UploadedFile::moveTo()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 8.6737
cc 5
eloc 11
nc 5
nop 1
crap 5
1
<?php
2
3
namespace Kambo\Http\Message;
4
5
// \Spl
6
use RuntimeException;
7
use InvalidArgumentException;
8
9
// \Psr
10
use Psr\Http\Message\UploadedFileInterface;
11
12
// \Http\Message
13
use Kambo\Http\Message\Stream;
14
use Kambo\Http\Message\Utils\UploadFile;
15
16
/**
17
 * Value object representing a file uploaded through an HTTP request.
18
 *
19
 * Instances of this interface are immutable; all methods that might change
20
 * state retain the internal state of the current instance and return
21
 * an instance that contains the changed state.
22
 *
23
 * @package Kambo\Http\Message
24
 * @author  Bohuslav Simek <[email protected]>
25
 * @license MIT
26
 */
27
class UploadedFile implements UploadedFileInterface
28
{
29
    /**
30
     * Flag signaling that the file has been moved
31
     *
32
     * @var boolean
33
     */
34
    private $moved = false;
35
36
    /**
37
     * Size of the file
38
     *
39
     * @var int|null
40
     */
41
    private $size;
42
43
    /**
44
     * File error code
45
     *
46
     * @var int
47
     */
48
    private $error;
49
50
    /**
51
     * The file media type provided by the client.
52
     *
53
     * @var string|null
54
     */
55
    private $clientMediaType;
56
57
    /**
58
     * The filename provided by the client.
59
     *
60
     * @var string|null
61
     */
62
    private $clientName;
63
64
    /**
65
     * The full path to the uploaded file provided by the client.
66
     *
67
     * @var string
68
     */
69
    private $file;
70
71
    /**
72
     * Stream representation of the uploaded file.
73
     *
74
     * @var StreamInterface
75
     */
76
    private $stream;
77
78
    /**
79
     * Simple wrapper for methods connected with upload eg.: is_uploaded_file
80
     *
81
     * @var UploadFile
82
     */
83
    private $uploadFile;
84
85
    /**
86
     * Construct a new UploadedFile instance.
87
     *
88
     * @param string      $file            The full path to the uploaded file provided by the client.
89
     * @param string|null $clientName      The filename provided by the client.
90
     * @param string|null $clientMediaType The file media type provided by the client.
91
     * @param int|null    $size            The file size in bytes.
92
     * @param int         $error           The UPLOAD_ERR_XXX code representing the status of the upload.
93
     */
94 11
    public function __construct(
95
        $file,
96
        $clientName,
97
        $clientMediaType,
98
        $size,
99
        $error,
100
        $uploadFile = null
101
    ) {
102 11
        $this->file            = $file;
103 11
        $this->clientName      = $clientName;
104 11
        $this->clientMediaType = $clientMediaType;
105 11
        $this->size            = $size;
106 11
        $this->error           = $error;
107
108 11
        if (isset($uploadFile)) {
109 4
            $this->uploadFile = $uploadFile;
110 4
        } else {
111 7
            $this->uploadFile = new UploadFile();
112
        }
113 11
    }
114
115
    /**
116
     * Retrieve a stream representing the uploaded file.
117
     *
118
     * This method return a StreamInterface instance, representing the
119
     * uploaded file. The purpose of this method is to allow utilizing native PHP
120
     * stream functionality to manipulate the file upload, such as
121
     * stream_copy_to_stream() (though the result will need to be decorated in a
122
     * native PHP stream wrapper to work with such functions).
123
     *
124
     * If the moveTo() method has been called previously, this method will raise
125
     * an exception.
126
     *
127
     * @return StreamInterface Stream representation of the uploaded file.
128
     *
129
     * @throws \RuntimeException in cases when no stream is available or can be
130
     *                           created.
131
     */
132 2
    public function getStream()
133
    {
134 2
        if ($this->moved) {
135 1
            throw new \RuntimeException(sprintf('Uploaded file %1s has already been moved', $this->clientName));
136
        }
137
138 1
        if ($this->stream === null) {
139 1
            $this->stream = new Stream(fopen($this->file, 'r'));
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Kambo\Http\Message\...open($this->file, 'r')) of type object<Kambo\Http\Message\Stream> is incompatible with the declared type object<Kambo\Http\Message\StreamInterface> of property $stream.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
140 1
        }
141
142 1
        return $this->stream;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->stream; of type Kambo\Http\Message\Strea...Message\StreamInterface adds the type Kambo\Http\Message\StreamInterface to the return on line 142 which is incompatible with the return type declared by the interface Psr\Http\Message\UploadedFileInterface::getStream of type Psr\Http\Message\StreamInterface.
Loading history...
143
    }
144
145
    /**
146
     * Move the uploaded file to a new location.
147
     *
148
     * Use this method as an alternative to move_uploaded_file(). This method is
149
     * guaranteed to work in both SAPI and non-SAPI environments.
150
     * Implementations must determine which environment they are in, and use the
151
     * appropriate method (move_uploaded_file(), rename(), or a stream
152
     * operation) to perform the operation.
153
     *
154
     * $targetPath may be an absolute path, or a relative path. If it is a
155
     * relative path, resolution should be the same as used by PHP's rename()
156
     * function.
157
     *
158
     * The original file or stream is removed on completion.
159
     *
160
     * If this method is called more than once, any subsequent calls MUST raise
161
     * an exception.
162
     *
163
     * When used in an SAPI environment where $_FILES is populated, when writing
164
     * files via moveTo(), is_uploaded_file() and move_uploaded_file() are
165
     * used to ensure permissions and upload status are verified correctly.
166
     *
167
     * If you wish to move to a stream, use getStream(), as SAPI operations
168
     * cannot guarantee writing to stream destinations.
169
     *
170
     * @see http://php.net/is_uploaded_file
171
     * @see http://php.net/move_uploaded_file
172
     *
173
     * @param string $targetPath Path to which to move the uploaded file.
174
     *
175
     * @throws \InvalidArgumentException if the $path specified is invalid.
176
     * @throws \RuntimeException         on any error during the move operation, or on
177
     *                                   the second or subsequent call to the method.
178
     */
179 6
    public function moveTo($targetPath)
180
    {
181 6
        if ($this->moved) {
182 1
            throw new RuntimeException('Uploaded file already moved');
183
        }
184
185 6
        if (!is_writable(dirname($targetPath))) {
186 1
            throw new InvalidArgumentException('Upload target path is not writable');
187
        }
188
189 5
        if (!$this->uploadFile->is($this->file)) {
190 1
            throw new RuntimeException(sprintf('%1s is not a valid uploaded file', $this->file));
191
        }
192
193 4
        if (!$this->uploadFile->move($this->file, $targetPath)) {
194 1
            throw new RuntimeException(
195 1
                sprintf('Error moving uploaded file %1s to %2s', $this->clientName, $targetPath)
196 1
            );
197
        }
198
199 3
        $this->moved = true;
200 3
    }
201
202
    /**
203
     * Retrieve the file size.
204
     *
205
     * Implementations returns the value stored in the "size" key of
206
     * the file in the $_FILES array if available, as PHP calculates this based
207
     * on the actual size transmitted.
208
     *
209
     * @return int|null The file size in bytes or null if unknown.
210
     */
211 3
    public function getSize()
212
    {
213 3
        return $this->size;
214
    }
215
216
    /**
217
     * Retrieve the error associated with the uploaded file.
218
     *
219
     * The return value is one of PHP's UPLOAD_ERR_XXX constants.
220
     *
221
     * If the file was uploaded successfully, this method return UPLOAD_ERR_OK.
222
     *
223
     * Implementation returns the value stored in the "error" key of
224
     * the file in the $_FILES array.
225
     *
226
     * @see http://php.net/manual/en/features.file-upload.errors.php
227
     *
228
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
229
     */
230 3
    public function getError()
231
    {
232 3
        return $this->error;
233
    }
234
235
    /**
236
     * Retrieve the filename sent by the client.
237
     *
238
     * Do not trust the value returned by this method. A client could send
239
     * a malicious filename with the intention to corrupt or hack your
240
     * application.
241
     *
242
     * Implementation returns the value stored in the "name" key of
243
     * the file in the $_FILES array.
244
     *
245
     * @return string|null The filename sent by the client or null if none
246
     *                     was provided.
247
     */
248 3
    public function getClientFilename()
249
    {
250 3
        return $this->clientName;
251
    }
252
253
    /**
254
     * Retrieve the media type sent by the client.
255
     *
256
     * Do not trust the value returned by this method. A client could send
257
     * a malicious media type with the intention to corrupt or hack your
258
     * application.
259
     *
260
     * Implementation returns the value stored in the "type" key of
261
     * the file in the $_FILES array.
262
     *
263
     * @return string|null The media type sent by the client or null if none
264
     *                     was provided.
265
     */
266 3
    public function getClientMediaType()
267
    {
268 3
        return $this->clientMediaType;
269
    }
270
}
271