Completed
Push — master ( c448e0...8d38f7 )
by Tobias
19:28 queued 17:43
created

UploadedFile::setError()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.8666
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Nyholm\Psr7;
6
7
use Psr\Http\Message\{StreamInterface, UploadedFileInterface};
8
9
/**
10
 * @author Michael Dowling and contributors to guzzlehttp/psr7
11
 * @author Tobias Nyholm <[email protected]>
12
 * @author Martijn van der Ven <[email protected]>
13
 */
14
final class UploadedFile implements UploadedFileInterface
15
{
16
    /** @var array */
17
    private const ERRORS = [
18
        \UPLOAD_ERR_OK => 1,
19
        \UPLOAD_ERR_INI_SIZE => 1,
20
        \UPLOAD_ERR_FORM_SIZE => 1,
21
        \UPLOAD_ERR_PARTIAL => 1,
22
        \UPLOAD_ERR_NO_FILE => 1,
23
        \UPLOAD_ERR_NO_TMP_DIR => 1,
24
        \UPLOAD_ERR_CANT_WRITE => 1,
25
        \UPLOAD_ERR_EXTENSION => 1,
26
    ];
27
28
    /** @var string */
29
    private $clientFilename;
30
31
    /** @var string */
32
    private $clientMediaType;
33
34
    /** @var int */
35
    private $error;
36
37
    /** @var null|string */
38
    private $file;
39
40
    /** @var bool */
41
    private $moved = false;
42
43
    /** @var null|int */
44
    private $size;
45
46
    /** @var null|StreamInterface */
47
    private $stream;
48
49
    /**
50
     * @param StreamInterface|string|resource $streamOrFile
51
     * @param int                             $size
52
     * @param int                             $errorStatus
53
     * @param string|null                     $clientFilename
54
     * @param string|null                     $clientMediaType
55
     */
56 75
    public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
57
    {
58 75
        $this->setError($errorStatus);
59 66
        $this->setSize($size);
60 66
        $this->setClientFilename($clientFilename);
61 60
        $this->setClientMediaType($clientMediaType);
62
63 54
        if ($this->isOk()) {
64 32
            $this->setStreamOrFile($streamOrFile);
65
        }
66 47
    }
67
68
    /**
69
     * Depending on the value set file or stream variable.
70
     *
71
     * @param string|resource|StreamInterface $streamOrFile
72
     *
73
     * @throws \InvalidArgumentException
74
     */
75 32
    private function setStreamOrFile($streamOrFile): void
76
    {
77 32
        if (\is_string($streamOrFile)) {
78 1
            $this->file = $streamOrFile;
79 31
        } elseif (\is_resource($streamOrFile)) {
80 1
            $this->stream = Stream::create($streamOrFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Nyholm\Psr7\Stream::create($streamOrFile) of type object<Psr\Http\Message\StreamInterface> is incompatible with the declared type null|object<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...
81 30
        } elseif ($streamOrFile instanceof StreamInterface) {
82 23
            $this->stream = $streamOrFile;
83
        } else {
84 7
            throw new \InvalidArgumentException('Invalid stream or file provided for UploadedFile');
85
        }
86 25
    }
87
88 75
    private function setError($error): void
89
    {
90 75
        if (false === \is_int($error)) {
91 7
            throw new \InvalidArgumentException('Upload file error status must be an integer');
92
        }
93
94 68
        if (!isset(self::ERRORS[$error])) {
95 2
            throw new \InvalidArgumentException('Invalid error status for UploadedFile');
96
        }
97
98 66
        $this->error = $error;
99 66
    }
100
101 66
    private function setSize($size): void
102
    {
103 66
        if (false === \is_int($size)) {
104
            throw new \InvalidArgumentException('Upload file size must be an integer');
105
        }
106
107 66
        $this->size = $size;
108 66
    }
109
110 66
    private function setClientFilename($clientFilename): void
111
    {
112 66
        if ($clientFilename !== null && !\is_string($clientFilename)) {
113 6
            throw new \InvalidArgumentException('Upload file client filename must be a string or null');
114
        }
115
116 60
        $this->clientFilename = $clientFilename;
117 60
    }
118
119 60
    private function setClientMediaType($clientMediaType): void
120
    {
121 60
        if ($clientMediaType !== null && !\is_string($clientMediaType)) {
122 6
            throw new \InvalidArgumentException('Upload file client media type must be a string or null');
123
        }
124
125 54
        $this->clientMediaType = $clientMediaType;
126 54
    }
127
128
    /**
129
     * @return bool return true if there is no upload error
130
     */
131 54
    private function isOk(): bool
132
    {
133 54
        return \UPLOAD_ERR_OK === $this->error;
134
    }
135
136
    /**
137
     * @throws \RuntimeException if is moved or not ok
138
     */
139 34
    private function validateActive(): void
140
    {
141 34
        if (false === $this->isOk()) {
142 14
            throw new \RuntimeException('Cannot retrieve stream due to upload error');
143
        }
144
145 20
        if ($this->moved) {
146 4
            throw new \RuntimeException('Cannot retrieve stream after it has already been moved');
147
        }
148 20
    }
149
150 18
    public function getStream(): StreamInterface
151
    {
152 18
        $this->validateActive();
153
154 11
        if ($this->stream instanceof StreamInterface) {
155 11
            return $this->stream;
156
        }
157
158
        $resource = \fopen($this->file, 'r');
159
160
        return Stream::create($resource);
161
    }
162
163 23
    public function moveTo($targetPath): void
164
    {
165 23
        $this->validateActive();
166
167 16
        if (!\is_string($targetPath) || $targetPath === '') {
168 8
            throw new \InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string');
169
        }
170
171 8
        if (null !== $this->file) {
172 1
            $this->moved = 'cli' === PHP_SAPI ? \rename($this->file, $targetPath) : \move_uploaded_file($this->file, $targetPath);
173
        } else {
174 7
            $stream = $this->getStream();
175 7
            if ($stream->isSeekable()) {
176 7
                $stream->rewind();
177
            }
178 7
            $this->copyToStream($stream, Stream::create(\fopen($targetPath, 'w')));
179 7
            $this->moved = true;
180
        }
181
182 8
        if (false === $this->moved) {
183
            throw new \RuntimeException(\sprintf('Uploaded file could not be moved to %s', $targetPath));
184
        }
185 8
    }
186
187 3
    public function getSize(): ?int
188
    {
189 3
        return $this->size;
190
    }
191
192 10
    public function getError(): int
193
    {
194 10
        return $this->error;
195
    }
196
197 3
    public function getClientFilename(): ?string
198
    {
199 3
        return $this->clientFilename;
200
    }
201
202 3
    public function getClientMediaType(): ?string
203
    {
204 3
        return $this->clientMediaType;
205
    }
206
207
    /**
208
     * Copy the contents of a stream into another stream until the given number
209
     * of bytes have been read.
210
     *
211
     * @author Michael Dowling and contributors to guzzlehttp/psr7
212
     *
213
     * @param StreamInterface $source Stream to read from
214
     * @param StreamInterface $dest   Stream to write to
215
     * @param int             $maxLen Maximum number of bytes to read. Pass -1
216
     *                                to read the entire stream
217
     *
218
     * @throws \RuntimeException on error
219
     */
220 7
    private function copyToStream(StreamInterface $source, StreamInterface $dest, $maxLen = -1): void
221
    {
222 7
        if ($maxLen === -1) {
223 7
            while (!$source->eof()) {
224 7
                if (!$dest->write($source->read(1048576))) {
225 7
                    break;
226
                }
227
            }
228
229 7
            return;
230
        }
231
232
        $bytes = 0;
233
        while (!$source->eof()) {
234
            $buf = $source->read($maxLen - $bytes);
235
            if (!($len = \strlen($buf))) {
236
                break;
237
            }
238
            $bytes += $len;
239
            $dest->write($buf);
240
            if ($bytes === $maxLen) {
241
                break;
242
            }
243
        }
244
    }
245
}
246