UploadedFile::moveTo()   B
last analyzed

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 0
Metric Value
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 8.6737
c 0
b 0
f 0
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 \Psr\Http\Message\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
     * @param UploadFile|null $uploadFile      Helper for manipulating with uploaded files.
94
     */
95 11
    public function __construct(
96
        $file,
97
        $clientName,
98
        $clientMediaType,
99
        $size,
100
        $error,
101
        $uploadFile = null
102
    ) {
103 11
        $this->file            = $file;
104 11
        $this->clientName      = $clientName;
105 11
        $this->clientMediaType = $clientMediaType;
106 11
        $this->size            = $size;
107 11
        $this->error           = $error;
108
109 11
        if (isset($uploadFile)) {
110 4
            $this->uploadFile = $uploadFile;
111 4
        } else {
112 7
            $this->uploadFile = new UploadFile();
113
        }
114 11
    }
115
116
    /**
117
     * Retrieve a stream representing the uploaded file.
118
     *
119
     * This method return a StreamInterface instance, representing the
120
     * uploaded file. The purpose of this method is to allow utilizing native PHP
121
     * stream functionality to manipulate the file upload, such as
122
     * stream_copy_to_stream() (though the result will need to be decorated in a
123
     * native PHP stream wrapper to work with such functions).
124
     *
125
     * If the moveTo() method has been called previously, this method will raise
126
     * an exception.
127
     *
128
     * @return StreamInterface Stream representation of the uploaded file.
129
     *
130
     * @throws \RuntimeException in cases when no stream is available or can be
131
     *                           created.
132
     */
133 2
    public function getStream()
134
    {
135 2
        if ($this->moved) {
136 1
            throw new \RuntimeException(sprintf('Uploaded file %1s has already been moved', $this->clientName));
137
        }
138
139 1
        if ($this->stream === null) {
140 1
            $this->stream = new Stream(fopen($this->file, 'r'));
141 1
        }
142
143 1
        return $this->stream;
144
    }
145
146
    /**
147
     * Move the uploaded file to a new location.
148
     *
149
     * Use this method as an alternative to move_uploaded_file(). This method is
150
     * guaranteed to work in both SAPI and non-SAPI environments.
151
     * Implementations must determine which environment they are in, and use the
152
     * appropriate method (move_uploaded_file(), rename(), or a stream
153
     * operation) to perform the operation.
154
     *
155
     * $targetPath may be an absolute path, or a relative path. If it is a
156
     * relative path, resolution should be the same as used by PHP's rename()
157
     * function.
158
     *
159
     * The original file or stream is removed on completion.
160
     *
161
     * If this method is called more than once, any subsequent calls MUST raise
162
     * an exception.
163
     *
164
     * When used in an SAPI environment where $_FILES is populated, when writing
165
     * files via moveTo(), is_uploaded_file() and move_uploaded_file() are
166
     * used to ensure permissions and upload status are verified correctly.
167
     *
168
     * If you wish to move to a stream, use getStream(), as SAPI operations
169
     * cannot guarantee writing to stream destinations.
170
     *
171
     * @see http://php.net/is_uploaded_file
172
     * @see http://php.net/move_uploaded_file
173
     *
174
     * @param string $targetPath Path to which to move the uploaded file.
175
     *
176
     * @throws \InvalidArgumentException if the $path specified is invalid.
177
     * @throws \RuntimeException         on any error during the move operation, or on
178
     *                                   the second or subsequent call to the method.
179
     */
180 6
    public function moveTo($targetPath)
181
    {
182 6
        if ($this->moved) {
183 1
            throw new RuntimeException('Uploaded file already moved');
184
        }
185
186 6
        if (!is_writable(dirname($targetPath))) {
187 1
            throw new InvalidArgumentException('Upload target path is not writable');
188
        }
189
190 5
        if (!$this->uploadFile->is($this->file)) {
191 1
            throw new RuntimeException(sprintf('%1s is not a valid uploaded file', $this->file));
192
        }
193
194 4
        if (!$this->uploadFile->move($this->file, $targetPath)) {
195 1
            throw new RuntimeException(
196 1
                sprintf('Error moving uploaded file %1s to %2s', $this->clientName, $targetPath)
197 1
            );
198
        }
199
200 3
        $this->moved = true;
201 3
    }
202
203
    /**
204
     * Retrieve the file size.
205
     *
206
     * Implementations returns the value stored in the "size" key of
207
     * the file in the $_FILES array if available, as PHP calculates this based
208
     * on the actual size transmitted.
209
     *
210
     * @return int|null The file size in bytes or null if unknown.
211
     */
212 3
    public function getSize()
213
    {
214 3
        return $this->size;
215
    }
216
217
    /**
218
     * Retrieve the error associated with the uploaded file.
219
     *
220
     * The return value is one of PHP's UPLOAD_ERR_XXX constants.
221
     *
222
     * If the file was uploaded successfully, this method return UPLOAD_ERR_OK.
223
     *
224
     * Implementation returns the value stored in the "error" key of
225
     * the file in the $_FILES array.
226
     *
227
     * @see http://php.net/manual/en/features.file-upload.errors.php
228
     *
229
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
230
     */
231 3
    public function getError()
232
    {
233 3
        return $this->error;
234
    }
235
236
    /**
237
     * Retrieve the filename sent by the client.
238
     *
239
     * Do not trust the value returned by this method. A client could send
240
     * a malicious filename with the intention to corrupt or hack your
241
     * application.
242
     *
243
     * Implementation returns the value stored in the "name" key of
244
     * the file in the $_FILES array.
245
     *
246
     * @return string|null The filename sent by the client or null if none
247
     *                     was provided.
248
     */
249 3
    public function getClientFilename()
250
    {
251 3
        return $this->clientName;
252
    }
253
254
    /**
255
     * Retrieve the media type sent by the client.
256
     *
257
     * Do not trust the value returned by this method. A client could send
258
     * a malicious media type with the intention to corrupt or hack your
259
     * application.
260
     *
261
     * Implementation returns the value stored in the "type" key of
262
     * the file in the $_FILES array.
263
     *
264
     * @return string|null The media type sent by the client or null if none
265
     *                     was provided.
266
     */
267 3
    public function getClientMediaType()
268
    {
269 3
        return $this->clientMediaType;
270
    }
271
}
272