annotate vendor/zendframework/zend-diactoros/src/UploadedFile.php @ 19:fa3358dc1485 tip

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