c260b0c13c21eba22d43fd5e5029cffb3f2ad1e8
[yaffs-website] / vendor / zendframework / zend-diactoros / src / UploadedFile.php
1 <?php
2 /**
3  * @see       https://github.com/zendframework/zend-diactoros for the canonical source repository
4  * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
5  * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
6  */
7
8 namespace Zend\Diactoros;
9
10 use InvalidArgumentException;
11 use Psr\Http\Message\StreamInterface;
12 use Psr\Http\Message\UploadedFileInterface;
13 use RuntimeException;
14
15 use function dirname;
16 use function fclose;
17 use function fopen;
18 use function fwrite;
19 use function is_dir;
20 use function is_int;
21 use function is_resource;
22 use function is_string;
23 use function is_writable;
24 use function move_uploaded_file;
25 use function sprintf;
26 use function strpos;
27
28 use const PHP_SAPI;
29 use const UPLOAD_ERR_CANT_WRITE;
30 use const UPLOAD_ERR_EXTENSION;
31 use const UPLOAD_ERR_FORM_SIZE;
32 use const UPLOAD_ERR_INI_SIZE;
33 use const UPLOAD_ERR_NO_FILE;
34 use const UPLOAD_ERR_NO_TMP_DIR;
35 use const UPLOAD_ERR_OK;
36 use const UPLOAD_ERR_PARTIAL;
37
38 class UploadedFile implements UploadedFileInterface
39 {
40     const ERROR_MESSAGES = [
41         UPLOAD_ERR_OK         => 'There is no error, the file uploaded with success',
42         UPLOAD_ERR_INI_SIZE   => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
43         UPLOAD_ERR_FORM_SIZE  => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was '
44             . 'specified in the HTML form',
45         UPLOAD_ERR_PARTIAL    => 'The uploaded file was only partially uploaded',
46         UPLOAD_ERR_NO_FILE    => 'No file was uploaded',
47         UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder',
48         UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
49         UPLOAD_ERR_EXTENSION  => 'A PHP extension stopped the file upload.',
50     ];
51
52     /**
53      * @var string|null
54      */
55     private $clientFilename;
56
57     /**
58      * @var string|null
59      */
60     private $clientMediaType;
61
62     /**
63      * @var int
64      */
65     private $error;
66
67     /**
68      * @var null|string
69      */
70     private $file;
71
72     /**
73      * @var bool
74      */
75     private $moved = false;
76
77     /**
78      * @var int
79      */
80     private $size;
81
82     /**
83      * @var null|StreamInterface
84      */
85     private $stream;
86
87     /**
88      * @param string|resource $streamOrFile
89      * @param int $size
90      * @param int $errorStatus
91      * @param string|null $clientFilename
92      * @param string|null $clientMediaType
93      * @throws InvalidArgumentException
94      */
95     public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
96     {
97         if ($errorStatus === UPLOAD_ERR_OK) {
98             if (is_string($streamOrFile)) {
99                 $this->file = $streamOrFile;
100             }
101             if (is_resource($streamOrFile)) {
102                 $this->stream = new Stream($streamOrFile);
103             }
104
105             if (! $this->file && ! $this->stream) {
106                 if (! $streamOrFile instanceof StreamInterface) {
107                     throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile');
108                 }
109                 $this->stream = $streamOrFile;
110             }
111         }
112
113         if (! is_int($size)) {
114             throw new InvalidArgumentException('Invalid size provided for UploadedFile; must be an int');
115         }
116         $this->size = $size;
117
118         if (! is_int($errorStatus)
119             || 0 > $errorStatus
120             || 8 < $errorStatus
121         ) {
122             throw new InvalidArgumentException(
123                 'Invalid error status for UploadedFile; must be an UPLOAD_ERR_* constant'
124             );
125         }
126         $this->error = $errorStatus;
127
128         if (null !== $clientFilename && ! is_string($clientFilename)) {
129             throw new InvalidArgumentException(
130                 'Invalid client filename provided for UploadedFile; must be null or a string'
131             );
132         }
133         $this->clientFilename = $clientFilename;
134
135         if (null !== $clientMediaType && ! is_string($clientMediaType)) {
136             throw new InvalidArgumentException(
137                 'Invalid client media type provided for UploadedFile; must be null or a string'
138             );
139         }
140         $this->clientMediaType = $clientMediaType;
141     }
142
143     /**
144      * {@inheritdoc}
145      * @throws \RuntimeException if the upload was not successful.
146      */
147     public function getStream()
148     {
149         if ($this->error !== UPLOAD_ERR_OK) {
150             throw new RuntimeException(sprintf(
151                 'Cannot retrieve stream due to upload error: %s',
152                 self::ERROR_MESSAGES[$this->error]
153             ));
154         }
155
156         if ($this->moved) {
157             throw new RuntimeException('Cannot retrieve stream after it has already been moved');
158         }
159
160         if ($this->stream instanceof StreamInterface) {
161             return $this->stream;
162         }
163
164         $this->stream = new Stream($this->file);
165         return $this->stream;
166     }
167
168     /**
169      * {@inheritdoc}
170      *
171      * @see http://php.net/is_uploaded_file
172      * @see http://php.net/move_uploaded_file
173      * @param string $targetPath Path to which to move the uploaded file.
174      * @throws \RuntimeException if the upload was not successful.
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     public function moveTo($targetPath)
180     {
181         if ($this->moved) {
182             throw new RuntimeException('Cannot move file; already moved!');
183         }
184
185         if ($this->error !== UPLOAD_ERR_OK) {
186             throw new RuntimeException(sprintf(
187                 'Cannot retrieve stream due to upload error: %s',
188                 self::ERROR_MESSAGES[$this->error]
189             ));
190         }
191
192         if (! is_string($targetPath) || empty($targetPath)) {
193             throw new InvalidArgumentException(
194                 'Invalid path provided for move operation; must be a non-empty string'
195             );
196         }
197
198         $targetDirectory = dirname($targetPath);
199         if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) {
200             throw new RuntimeException(sprintf(
201                 'The target directory `%s` does not exists or is not writable',
202                 $targetDirectory
203             ));
204         }
205
206         $sapi = PHP_SAPI;
207         switch (true) {
208             case (empty($sapi) || 0 === strpos($sapi, 'cli') || ! $this->file):
209                 // Non-SAPI environment, or no filename present
210                 $this->writeFile($targetPath);
211                 break;
212             default:
213                 // SAPI environment, with file present
214                 if (false === move_uploaded_file($this->file, $targetPath)) {
215                     throw new RuntimeException('Error occurred while moving uploaded file');
216                 }
217                 break;
218         }
219
220         $this->moved = true;
221     }
222
223     /**
224      * {@inheritdoc}
225      *
226      * @return int|null The file size in bytes or null if unknown.
227      */
228     public function getSize()
229     {
230         return $this->size;
231     }
232
233     /**
234      * {@inheritdoc}
235      *
236      * @see http://php.net/manual/en/features.file-upload.errors.php
237      * @return int One of PHP's UPLOAD_ERR_XXX constants.
238      */
239     public function getError()
240     {
241         return $this->error;
242     }
243
244     /**
245      * {@inheritdoc}
246      *
247      * @return string|null The filename sent by the client or null if none
248      *     was provided.
249      */
250     public function getClientFilename()
251     {
252         return $this->clientFilename;
253     }
254
255     /**
256      * {@inheritdoc}
257      */
258     public function getClientMediaType()
259     {
260         return $this->clientMediaType;
261     }
262
263     /**
264      * Write internal stream to given path
265      *
266      * @param string $path
267      */
268     private function writeFile($path)
269     {
270         $handle = fopen($path, 'wb+');
271         if (false === $handle) {
272             throw new RuntimeException('Unable to write to designated path');
273         }
274
275         $stream = $this->getStream();
276         $stream->rewind();
277         while (! $stream->eof()) {
278             fwrite($handle, $stream->read(4096));
279         }
280
281         fclose($handle);
282     }
283 }