These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Nyholm\Psr7; |
||
6 | |||
7 | use InvalidArgumentException; |
||
8 | use Nyholm\Psr7\Factory\StreamFactory; |
||
9 | use Psr\Http\Message\StreamInterface; |
||
10 | use Psr\Http\Message\UploadedFileInterface; |
||
11 | use RuntimeException; |
||
12 | |||
13 | /** |
||
14 | * @author Michael Dowling and contributors to guzzlehttp/psr7 |
||
15 | * @author Tobias Nyholm <[email protected]> |
||
16 | */ |
||
17 | class UploadedFile implements UploadedFileInterface |
||
18 | { |
||
19 | /** @var int[] */ |
||
20 | private static $errors = [ |
||
21 | UPLOAD_ERR_OK, UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, |
||
22 | UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION, |
||
23 | ]; |
||
24 | |||
25 | /** @var string */ |
||
26 | private $clientFilename; |
||
27 | |||
28 | /** @var string */ |
||
29 | private $clientMediaType; |
||
30 | |||
31 | /** @var int */ |
||
32 | private $error; |
||
33 | |||
34 | /** @var null|string */ |
||
35 | private $file; |
||
36 | |||
37 | /** @var bool */ |
||
38 | private $moved = false; |
||
39 | |||
40 | /** @var int */ |
||
41 | private $size; |
||
42 | |||
43 | /** @var StreamInterface|null */ |
||
44 | private $stream; |
||
45 | |||
46 | /** |
||
47 | * @param StreamInterface|string|resource $streamOrFile |
||
48 | * @param int $size |
||
49 | * @param int $errorStatus |
||
50 | * @param string|null $clientFilename |
||
51 | * @param string|null $clientMediaType |
||
52 | */ |
||
53 | 70 | public function __construct( |
|
54 | $streamOrFile, |
||
55 | $size, |
||
56 | $errorStatus, |
||
57 | $clientFilename = null, |
||
58 | $clientMediaType = null |
||
59 | ) { |
||
60 | 70 | $this->setError($errorStatus); |
|
61 | 61 | $this->setSize($size); |
|
62 | 57 | $this->setClientFilename($clientFilename); |
|
63 | 51 | $this->setClientMediaType($clientMediaType); |
|
64 | |||
65 | 45 | if ($this->isOk()) { |
|
66 | 23 | $this->setStreamOrFile($streamOrFile); |
|
67 | } |
||
68 | 38 | } |
|
69 | |||
70 | /** |
||
71 | * Depending on the value set file or stream variable. |
||
72 | * |
||
73 | * @param mixed $streamOrFile |
||
74 | * |
||
75 | * @throws InvalidArgumentException |
||
76 | */ |
||
77 | 23 | private function setStreamOrFile($streamOrFile) |
|
78 | { |
||
79 | 23 | if (is_string($streamOrFile)) { |
|
80 | 2 | $this->file = $streamOrFile; |
|
81 | 21 | } elseif (is_resource($streamOrFile)) { |
|
82 | 2 | $this->stream = Stream::createFromResource($streamOrFile); |
|
0 ignored issues
–
show
|
|||
83 | 19 | } elseif ($streamOrFile instanceof StreamInterface) { |
|
84 | 12 | $this->stream = $streamOrFile; |
|
85 | } else { |
||
86 | 7 | throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile'); |
|
87 | } |
||
88 | 16 | } |
|
89 | |||
90 | /** |
||
91 | * @param int $error |
||
92 | * |
||
93 | * @throws InvalidArgumentException |
||
94 | */ |
||
95 | 70 | private function setError($error) |
|
96 | { |
||
97 | 70 | if (false === is_int($error)) { |
|
98 | 7 | throw new InvalidArgumentException('Upload file error status must be an integer'); |
|
99 | } |
||
100 | |||
101 | 63 | if (false === in_array($error, self::$errors)) { |
|
102 | 2 | throw new InvalidArgumentException('Invalid error status for UploadedFile'); |
|
103 | } |
||
104 | |||
105 | 61 | $this->error = $error; |
|
106 | 61 | } |
|
107 | |||
108 | /** |
||
109 | * @param int $size |
||
110 | * |
||
111 | * @throws InvalidArgumentException |
||
112 | */ |
||
113 | 61 | private function setSize($size) |
|
114 | { |
||
115 | 61 | if (false === is_int($size)) { |
|
116 | 4 | throw new InvalidArgumentException('Upload file size must be an integer'); |
|
117 | } |
||
118 | |||
119 | 57 | $this->size = $size; |
|
120 | 57 | } |
|
121 | |||
122 | /** |
||
123 | * @param mixed $param |
||
124 | * |
||
125 | * @return bool |
||
126 | */ |
||
127 | 57 | private function isStringOrNull($param) |
|
128 | { |
||
129 | 57 | return in_array(gettype($param), ['string', 'NULL']); |
|
130 | } |
||
131 | |||
132 | /** |
||
133 | * @param mixed $param |
||
134 | * |
||
135 | * @return bool |
||
136 | */ |
||
137 | 12 | private function isStringNotEmpty($param) |
|
138 | { |
||
139 | 12 | return is_string($param) && false === empty($param); |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * @param string|null $clientFilename |
||
144 | * |
||
145 | * @throws InvalidArgumentException |
||
146 | */ |
||
147 | 57 | private function setClientFilename($clientFilename) |
|
148 | { |
||
149 | 57 | if (false === $this->isStringOrNull($clientFilename)) { |
|
150 | 6 | throw new InvalidArgumentException('Upload file client filename must be a string or null'); |
|
151 | } |
||
152 | |||
153 | 51 | $this->clientFilename = $clientFilename; |
|
154 | 51 | } |
|
155 | |||
156 | /** |
||
157 | * @param string|null $clientMediaType |
||
158 | * |
||
159 | * @throws InvalidArgumentException |
||
160 | */ |
||
161 | 51 | private function setClientMediaType($clientMediaType) |
|
162 | { |
||
163 | 51 | if (false === $this->isStringOrNull($clientMediaType)) { |
|
164 | 6 | throw new InvalidArgumentException('Upload file client media type must be a string or null'); |
|
165 | } |
||
166 | |||
167 | 45 | $this->clientMediaType = $clientMediaType; |
|
168 | 45 | } |
|
169 | |||
170 | /** |
||
171 | * @return bool Return true if there is no upload error. |
||
172 | */ |
||
173 | 45 | private function isOk() |
|
174 | { |
||
175 | 45 | return UPLOAD_ERR_OK === $this->error; |
|
176 | } |
||
177 | |||
178 | /** |
||
179 | * @throws RuntimeException if is moved or not ok |
||
180 | */ |
||
181 | 30 | private function validateActive() |
|
182 | { |
||
183 | 30 | if (false === $this->isOk()) { |
|
184 | 14 | throw new RuntimeException('Cannot retrieve stream due to upload error'); |
|
185 | } |
||
186 | |||
187 | 16 | if ($this->moved) { |
|
188 | 2 | throw new RuntimeException('Cannot retrieve stream after it has already been moved'); |
|
189 | } |
||
190 | 16 | } |
|
191 | |||
192 | 14 | public function getStream() |
|
193 | { |
||
194 | 14 | $this->validateActive(); |
|
195 | |||
196 | 7 | if ($this->stream instanceof StreamInterface) { |
|
197 | 6 | return $this->stream; |
|
198 | } |
||
199 | |||
200 | 1 | $resource = fopen($this->file, 'r'); |
|
201 | |||
202 | 1 | return Stream::createFromResource($resource); |
|
0 ignored issues
–
show
The return type of
return \Nyholm\Psr7\Stre...romResource($resource); (self ) is incompatible with the return type declared by the interface Psr\Http\Message\UploadedFileInterface::getStream of type Psr\Http\Message\StreamInterface .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function
Loading history...
|
|||
203 | } |
||
204 | |||
205 | 19 | public function moveTo($targetPath) |
|
206 | { |
||
207 | 19 | $this->validateActive(); |
|
208 | |||
209 | 12 | if (false === $this->isStringNotEmpty($targetPath)) { |
|
210 | 8 | throw new InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string'); |
|
211 | } |
||
212 | |||
213 | 4 | if (null !== $this->file) { |
|
214 | 1 | $this->moved = 'cli' == php_sapi_name() |
|
215 | 1 | ? rename($this->file, $targetPath) |
|
216 | 1 | : move_uploaded_file($this->file, $targetPath); |
|
217 | } else { |
||
218 | 3 | $stream = $this->getStream(); |
|
219 | 3 | if ($stream->isSeekable()) { |
|
220 | 3 | $stream->rewind(); |
|
221 | } |
||
222 | 3 | (new StreamFactory())->copyToStream( |
|
223 | 3 | $stream, |
|
224 | 3 | Stream::createFromResource(fopen($targetPath, 'w')) |
|
225 | ); |
||
226 | |||
227 | 3 | $this->moved = true; |
|
228 | } |
||
229 | |||
230 | 4 | if (false === $this->moved) { |
|
231 | throw new RuntimeException(sprintf('Uploaded file could not be moved to %s', $targetPath)); |
||
232 | } |
||
233 | 4 | } |
|
234 | |||
235 | 3 | public function getSize() |
|
236 | { |
||
237 | 3 | return $this->size; |
|
238 | } |
||
239 | |||
240 | 10 | public function getError() |
|
241 | { |
||
242 | 10 | return $this->error; |
|
243 | } |
||
244 | |||
245 | 3 | public function getClientFilename() |
|
246 | { |
||
247 | 3 | return $this->clientFilename; |
|
248 | } |
||
249 | |||
250 | 3 | public function getClientMediaType() |
|
251 | { |
||
252 | 3 | return $this->clientMediaType; |
|
253 | } |
||
254 | } |
||
255 |
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..