File indexing completed on 2024-12-29 05:27:23

0001 <?php
0002 namespace GuzzleHttp\Psr7;
0003 
0004 use InvalidArgumentException;
0005 use Psr\Http\Message\StreamInterface;
0006 use Psr\Http\Message\UploadedFileInterface;
0007 use RuntimeException;
0008 
0009 class UploadedFile implements UploadedFileInterface
0010 {
0011     /**
0012      * @var int[]
0013      */
0014     private static $errors = [
0015         UPLOAD_ERR_OK,
0016         UPLOAD_ERR_INI_SIZE,
0017         UPLOAD_ERR_FORM_SIZE,
0018         UPLOAD_ERR_PARTIAL,
0019         UPLOAD_ERR_NO_FILE,
0020         UPLOAD_ERR_NO_TMP_DIR,
0021         UPLOAD_ERR_CANT_WRITE,
0022         UPLOAD_ERR_EXTENSION,
0023     ];
0024 
0025     /**
0026      * @var string
0027      */
0028     private $clientFilename;
0029 
0030     /**
0031      * @var string
0032      */
0033     private $clientMediaType;
0034 
0035     /**
0036      * @var int
0037      */
0038     private $error;
0039 
0040     /**
0041      * @var null|string
0042      */
0043     private $file;
0044 
0045     /**
0046      * @var bool
0047      */
0048     private $moved = false;
0049 
0050     /**
0051      * @var int
0052      */
0053     private $size;
0054 
0055     /**
0056      * @var StreamInterface|null
0057      */
0058     private $stream;
0059 
0060     /**
0061      * @param StreamInterface|string|resource $streamOrFile
0062      * @param int $size
0063      * @param int $errorStatus
0064      * @param string|null $clientFilename
0065      * @param string|null $clientMediaType
0066      */
0067     public function __construct(
0068         $streamOrFile,
0069         $size,
0070         $errorStatus,
0071         $clientFilename = null,
0072         $clientMediaType = null
0073     ) {
0074         $this->setError($errorStatus);
0075         $this->setSize($size);
0076         $this->setClientFilename($clientFilename);
0077         $this->setClientMediaType($clientMediaType);
0078 
0079         if ($this->isOk()) {
0080             $this->setStreamOrFile($streamOrFile);
0081         }
0082     }
0083 
0084     /**
0085      * Depending on the value set file or stream variable
0086      *
0087      * @param mixed $streamOrFile
0088      * @throws InvalidArgumentException
0089      */
0090     private function setStreamOrFile($streamOrFile)
0091     {
0092         if (is_string($streamOrFile)) {
0093             $this->file = $streamOrFile;
0094         } elseif (is_resource($streamOrFile)) {
0095             $this->stream = new Stream($streamOrFile);
0096         } elseif ($streamOrFile instanceof StreamInterface) {
0097             $this->stream = $streamOrFile;
0098         } else {
0099             throw new InvalidArgumentException(
0100                 'Invalid stream or file provided for UploadedFile'
0101             );
0102         }
0103     }
0104 
0105     /**
0106      * @param int $error
0107      * @throws InvalidArgumentException
0108      */
0109     private function setError($error)
0110     {
0111         if (false === is_int($error)) {
0112             throw new InvalidArgumentException(
0113                 'Upload file error status must be an integer'
0114             );
0115         }
0116 
0117         if (false === in_array($error, UploadedFile::$errors)) {
0118             throw new InvalidArgumentException(
0119                 'Invalid error status for UploadedFile'
0120             );
0121         }
0122 
0123         $this->error = $error;
0124     }
0125 
0126     /**
0127      * @param int $size
0128      * @throws InvalidArgumentException
0129      */
0130     private function setSize($size)
0131     {
0132         if (false === is_int($size)) {
0133             throw new InvalidArgumentException(
0134                 'Upload file size must be an integer'
0135             );
0136         }
0137 
0138         $this->size = $size;
0139     }
0140 
0141     /**
0142      * @param mixed $param
0143      * @return boolean
0144      */
0145     private function isStringOrNull($param)
0146     {
0147         return in_array(gettype($param), ['string', 'NULL']);
0148     }
0149 
0150     /**
0151      * @param mixed $param
0152      * @return boolean
0153      */
0154     private function isStringNotEmpty($param)
0155     {
0156         return is_string($param) && false === empty($param);
0157     }
0158 
0159     /**
0160      * @param string|null $clientFilename
0161      * @throws InvalidArgumentException
0162      */
0163     private function setClientFilename($clientFilename)
0164     {
0165         if (false === $this->isStringOrNull($clientFilename)) {
0166             throw new InvalidArgumentException(
0167                 'Upload file client filename must be a string or null'
0168             );
0169         }
0170 
0171         $this->clientFilename = $clientFilename;
0172     }
0173 
0174     /**
0175      * @param string|null $clientMediaType
0176      * @throws InvalidArgumentException
0177      */
0178     private function setClientMediaType($clientMediaType)
0179     {
0180         if (false === $this->isStringOrNull($clientMediaType)) {
0181             throw new InvalidArgumentException(
0182                 'Upload file client media type must be a string or null'
0183             );
0184         }
0185 
0186         $this->clientMediaType = $clientMediaType;
0187     }
0188 
0189     /**
0190      * Return true if there is no upload error
0191      *
0192      * @return boolean
0193      */
0194     private function isOk()
0195     {
0196         return $this->error === UPLOAD_ERR_OK;
0197     }
0198 
0199     /**
0200      * @return boolean
0201      */
0202     public function isMoved()
0203     {
0204         return $this->moved;
0205     }
0206 
0207     /**
0208      * @throws RuntimeException if is moved or not ok
0209      */
0210     private function validateActive()
0211     {
0212         if (false === $this->isOk()) {
0213             throw new RuntimeException('Cannot retrieve stream due to upload error');
0214         }
0215 
0216         if ($this->isMoved()) {
0217             throw new RuntimeException('Cannot retrieve stream after it has already been moved');
0218         }
0219     }
0220 
0221     /**
0222      * {@inheritdoc}
0223      * @throws RuntimeException if the upload was not successful.
0224      */
0225     public function getStream()
0226     {
0227         $this->validateActive();
0228 
0229         if ($this->stream instanceof StreamInterface) {
0230             return $this->stream;
0231         }
0232 
0233         return new LazyOpenStream($this->file, 'r+');
0234     }
0235 
0236     /**
0237      * {@inheritdoc}
0238      *
0239      * @see http://php.net/is_uploaded_file
0240      * @see http://php.net/move_uploaded_file
0241      * @param string $targetPath Path to which to move the uploaded file.
0242      * @throws RuntimeException if the upload was not successful.
0243      * @throws InvalidArgumentException if the $path specified is invalid.
0244      * @throws RuntimeException on any error during the move operation, or on
0245      *     the second or subsequent call to the method.
0246      */
0247     public function moveTo($targetPath)
0248     {
0249         $this->validateActive();
0250 
0251         if (false === $this->isStringNotEmpty($targetPath)) {
0252             throw new InvalidArgumentException(
0253                 'Invalid path provided for move operation; must be a non-empty string'
0254             );
0255         }
0256 
0257         if ($this->file) {
0258             $this->moved = php_sapi_name() == 'cli'
0259                 ? rename($this->file, $targetPath)
0260                 : move_uploaded_file($this->file, $targetPath);
0261         } else {
0262             copy_to_stream(
0263                 $this->getStream(),
0264                 new LazyOpenStream($targetPath, 'w')
0265             );
0266 
0267             $this->moved = true;
0268         }
0269 
0270         if (false === $this->moved) {
0271             throw new RuntimeException(
0272                 sprintf('Uploaded file could not be moved to %s', $targetPath)
0273             );
0274         }
0275     }
0276 
0277     /**
0278      * {@inheritdoc}
0279      *
0280      * @return int|null The file size in bytes or null if unknown.
0281      */
0282     public function getSize()
0283     {
0284         return $this->size;
0285     }
0286 
0287     /**
0288      * {@inheritdoc}
0289      *
0290      * @see http://php.net/manual/en/features.file-upload.errors.php
0291      * @return int One of PHP's UPLOAD_ERR_XXX constants.
0292      */
0293     public function getError()
0294     {
0295         return $this->error;
0296     }
0297 
0298     /**
0299      * {@inheritdoc}
0300      *
0301      * @return string|null The filename sent by the client or null if none
0302      *     was provided.
0303      */
0304     public function getClientFilename()
0305     {
0306         return $this->clientFilename;
0307     }
0308 
0309     /**
0310      * {@inheritdoc}
0311      */
0312     public function getClientMediaType()
0313     {
0314         return $this->clientMediaType;
0315     }
0316 }