wiki.techinc.nl/includes/libs/ParamValidator/Util/UploadedFile.php
Brad Jorsch ff70806ef1 ParamValidator: Flag as unstable for 1.34
Iea6d4a1d0 isn't likely to make it, and I don't want to have to
support the architectural mistakes it's fixing in 1.35.

Change-Id: Icd161779d4e2eb60c507a5a4400f9432741c72eb
2019-11-01 13:52:00 -04:00

139 lines
3.5 KiB
PHP

<?php
namespace Wikimedia\ParamValidator\Util;
use Psr\Http\Message\UploadedFileInterface;
use RuntimeException;
use Wikimedia\AtEase\AtEase;
/**
* A simple implementation of UploadedFileInterface
*
* This exists so ParamValidator needn't depend on any specific PSR-7
* implementation for a class implementing UploadedFileInterface. It shouldn't
* be used directly by other code, other than perhaps when implementing
* Callbacks::getUploadedFile() when another PSR-7 library is not already in use.
*
* @since 1.34
* @unstable
*/
class UploadedFile implements UploadedFileInterface {
/** @var array File data */
private $data;
/** @var bool */
private $fromUpload;
/** @var UploadedFileStream|null */
private $stream = null;
/** @var bool Whether moveTo() was called */
private $moved = false;
/**
* @param array $data Data from $_FILES
* @param bool $fromUpload Set false if using this task with data not from
* $_FILES. Intended for unit testing.
*/
public function __construct( array $data, $fromUpload = true ) {
$this->data = $data;
$this->fromUpload = $fromUpload;
}
/**
* Throw if there was an error
* @throws RuntimeException
*/
private function checkError() {
switch ( $this->data['error'] ) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_INI_SIZE:
throw new RuntimeException( 'Upload exceeded maximum size' );
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException( 'Upload exceeded form-specified maximum size' );
case UPLOAD_ERR_PARTIAL:
throw new RuntimeException( 'File was only partially uploaded' );
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException( 'No file was uploaded' );
case UPLOAD_ERR_NO_TMP_DIR:
throw new RuntimeException( 'PHP has no temporary folder for storing uploaded files' );
case UPLOAD_ERR_CANT_WRITE:
throw new RuntimeException( 'PHP was unable to save the uploaded file' );
case UPLOAD_ERR_EXTENSION:
throw new RuntimeException( 'A PHP extension stopped the file upload' );
default:
throw new RuntimeException( 'Unknown upload error code ' . $this->data['error'] );
}
if ( $this->moved ) {
throw new RuntimeException( 'File has already been moved' );
}
if ( !isset( $this->data['tmp_name'] ) || !file_exists( $this->data['tmp_name'] ) ) {
throw new RuntimeException( 'Uploaded file is missing' );
}
}
public function getStream() {
if ( $this->stream ) {
return $this->stream;
}
$this->checkError();
$this->stream = new UploadedFileStream( $this->data['tmp_name'] );
return $this->stream;
}
public function moveTo( $targetPath ) {
$this->checkError();
if ( $this->fromUpload && !is_uploaded_file( $this->data['tmp_name'] ) ) {
throw new RuntimeException( 'Specified file is not an uploaded file' );
}
error_clear_last();
$ret = AtEase::quietCall(
$this->fromUpload ? 'move_uploaded_file' : 'rename',
$this->data['tmp_name'],
$targetPath
);
if ( $ret === false ) {
$err = error_get_last();
throw new RuntimeException( "Move failed: " . ( $err['message'] ?? 'Unknown error' ) );
}
$this->moved = true;
if ( $this->stream ) {
$this->stream->close();
$this->stream = null;
}
}
public function getSize() {
return $this->data['size'] ?? null;
}
public function getError() {
return $this->data['error'] ?? UPLOAD_ERR_NO_FILE;
}
public function getClientFilename() {
$ret = $this->data['name'] ?? null;
return $ret === '' ? null : $ret;
}
public function getClientMediaType() {
$ret = $this->data['type'] ?? null;
return $ret === '' ? null : $ret;
}
}