b436a6dc54da4ed6fb5a314576110feaddb4503c
[lhc/web/wiklou.git] / includes / libs / ParamValidator / TypeDef / UploadDef.php
1 <?php
2
3 namespace Wikimedia\ParamValidator\TypeDef;
4
5 use Psr\Http\Message\UploadedFileInterface;
6 use Wikimedia\ParamValidator\TypeDef;
7 use Wikimedia\ParamValidator\Util\UploadedFile;
8 use Wikimedia\ParamValidator\ValidationException;
9
10 /**
11 * Type definition for upload types
12 *
13 * The result from validate() is an object implementing UploadedFileInterface.
14 *
15 * ValidationException codes:
16 * - 'badupload': The upload is not valid. No data.
17 * - 'badupload-inisize': The upload exceeded the maximum in php.ini. Data:
18 * - 'size': The configured size (in bytes).
19 * - 'badupload-formsize': The upload exceeded the maximum in the form post. No data.
20 * - 'badupload-partial': The file was only partially uploaded. No data.
21 * - 'badupload-nofile': There was no file. No data.
22 * - 'badupload-notmpdir': PHP has no temporary directory to store the upload. No data.
23 * - 'badupload-cantwrite': PHP could not store the upload. No data.
24 * - 'badupload-phpext': A PHP extension rejected the upload. No data.
25 * - 'badupload-notupload': The field was present in the submission but was not encoded as
26 * an upload. No data.
27 * - 'badupload-unknown': Some unknown PHP upload error code. Data:
28 * - 'code': The code.
29 *
30 * @since 1.34
31 */
32 class UploadDef extends TypeDef {
33
34 public function getValue( $name, array $settings, array $options ) {
35 $ret = $this->callbacks->getUploadedFile( $name, $options );
36
37 if ( $ret && $ret->getError() === UPLOAD_ERR_NO_FILE &&
38 !$this->callbacks->hasParam( $name, $options )
39 ) {
40 // This seems to be that the client explicitly specified "no file" for the field
41 // instead of just omitting the field completely. DWTM.
42 $ret = null;
43 } elseif ( !$ret && $this->callbacks->hasParam( $name, $options ) ) {
44 // The client didn't format their upload properly so it came in as an ordinary
45 // field. Convert it to an error.
46 $ret = new UploadedFile( [
47 'name' => '',
48 'type' => '',
49 'tmp_name' => '',
50 'error' => -42, // PHP's UPLOAD_ERR_* are all positive numbers.
51 'size' => 0,
52 ] );
53 }
54
55 return $ret;
56 }
57
58 /**
59 * Fetch the value of PHP's upload_max_filesize ini setting
60 *
61 * This method exists so it can be mocked by unit tests that can't
62 * affect ini_get() directly.
63 *
64 * @codeCoverageIgnore
65 * @return string|false
66 */
67 protected function getIniSize() {
68 return ini_get( 'upload_max_filesize' );
69 }
70
71 public function validate( $name, $value, array $settings, array $options ) {
72 static $codemap = [
73 -42 => 'notupload', // Local from getValue()
74 UPLOAD_ERR_FORM_SIZE => 'formsize',
75 UPLOAD_ERR_PARTIAL => 'partial',
76 UPLOAD_ERR_NO_FILE => 'nofile',
77 UPLOAD_ERR_NO_TMP_DIR => 'notmpdir',
78 UPLOAD_ERR_CANT_WRITE => 'cantwrite',
79 UPLOAD_ERR_EXTENSION => 'phpext',
80 ];
81
82 if ( !$value instanceof UploadedFileInterface ) {
83 // Err?
84 throw new ValidationException( $name, $value, $settings, 'badupload', [] );
85 }
86
87 $err = $value->getError();
88 if ( $err === UPLOAD_ERR_OK ) {
89 return $value;
90 } elseif ( $err === UPLOAD_ERR_INI_SIZE ) {
91 static $prefixes = [
92 'g' => 1024 ** 3,
93 'm' => 1024 ** 2,
94 'k' => 1024 ** 1,
95 ];
96 $size = $this->getIniSize();
97 $last = strtolower( substr( $size, -1 ) );
98 $size = intval( $size, 10 ) * ( $prefixes[$last] ?? 1 );
99 throw new ValidationException( $name, $value, $settings, 'badupload-inisize', [
100 'size' => $size,
101 ] );
102 } elseif ( isset( $codemap[$err] ) ) {
103 throw new ValidationException( $name, $value, $settings, 'badupload-' . $codemap[$err], [] );
104 } else {
105 throw new ValidationException( $name, $value, $settings, 'badupload-unknown', [
106 'code' => $err,
107 ] );
108 }
109 }
110
111 public function stringifyValue( $name, $value, array $settings, array $options ) {
112 // Not going to happen.
113 return null;
114 }
115
116 }