3 namespace Wikimedia\ParamValidator\Util
;
5 use Psr\Http\Message\UploadedFileInterface
;
7 use Wikimedia\AtEase\AtEase
;
10 * A simple implementation of UploadedFileInterface
12 * This exists so ParamValidator needn't depend on any specific PSR-7
13 * implementation for a class implementing UploadedFileInterface. It shouldn't
14 * be used directly by other code, other than perhaps when implementing
15 * Callbacks::getUploadedFile() when another PSR-7 library is not already in use.
20 class UploadedFile
implements UploadedFileInterface
{
22 /** @var array File data */
28 /** @var UploadedFileStream|null */
29 private $stream = null;
31 /** @var bool Whether moveTo() was called */
32 private $moved = false;
35 * @param array $data Data from $_FILES
36 * @param bool $fromUpload Set false if using this task with data not from
37 * $_FILES. Intended for unit testing.
39 public function __construct( array $data, $fromUpload = true ) {
41 $this->fromUpload
= $fromUpload;
45 * Throw if there was an error
46 * @throws RuntimeException
48 private function checkError() {
49 switch ( $this->data
['error'] ) {
53 case UPLOAD_ERR_INI_SIZE
:
54 throw new RuntimeException( 'Upload exceeded maximum size' );
56 case UPLOAD_ERR_FORM_SIZE
:
57 throw new RuntimeException( 'Upload exceeded form-specified maximum size' );
59 case UPLOAD_ERR_PARTIAL
:
60 throw new RuntimeException( 'File was only partially uploaded' );
62 case UPLOAD_ERR_NO_FILE
:
63 throw new RuntimeException( 'No file was uploaded' );
65 case UPLOAD_ERR_NO_TMP_DIR
:
66 throw new RuntimeException( 'PHP has no temporary folder for storing uploaded files' );
68 case UPLOAD_ERR_CANT_WRITE
:
69 throw new RuntimeException( 'PHP was unable to save the uploaded file' );
71 case UPLOAD_ERR_EXTENSION
:
72 throw new RuntimeException( 'A PHP extension stopped the file upload' );
75 throw new RuntimeException( 'Unknown upload error code ' . $this->data
['error'] );
79 throw new RuntimeException( 'File has already been moved' );
81 if ( !isset( $this->data
['tmp_name'] ) ||
!file_exists( $this->data
['tmp_name'] ) ) {
82 throw new RuntimeException( 'Uploaded file is missing' );
86 public function getStream() {
87 if ( $this->stream
) {
92 $this->stream
= new UploadedFileStream( $this->data
['tmp_name'] );
96 public function moveTo( $targetPath ) {
99 if ( $this->fromUpload
&& !is_uploaded_file( $this->data
['tmp_name'] ) ) {
100 throw new RuntimeException( 'Specified file is not an uploaded file' );
104 $ret = AtEase
::quietCall(
105 $this->fromUpload ?
'move_uploaded_file' : 'rename',
106 $this->data
['tmp_name'],
109 if ( $ret === false ) {
110 $err = error_get_last();
111 throw new RuntimeException( "Move failed: " . ( $err['message'] ??
'Unknown error' ) );
115 if ( $this->stream
) {
116 $this->stream
->close();
117 $this->stream
= null;
121 public function getSize() {
122 return $this->data
['size'] ??
null;
125 public function getError() {
126 return $this->data
['error'] ?? UPLOAD_ERR_NO_FILE
;
129 public function getClientFilename() {
130 $ret = $this->data
['name'] ??
null;
131 return $ret === '' ?
null : $ret;
134 public function getClientMediaType() {
135 $ret = $this->data
['type'] ??
null;
136 return $ret === '' ?
null : $ret;