ParamValidator: Flag as unstable for 1.34
[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 * @unstable
32 */
33 class UploadDef extends TypeDef {
34
35 public function getValue( $name, array $settings, array $options ) {
36 $ret = $this->callbacks->getUploadedFile( $name, $options );
37
38 if ( $ret && $ret->getError() === UPLOAD_ERR_NO_FILE &&
39 !$this->callbacks->hasParam( $name, $options )
40 ) {
41 // This seems to be that the client explicitly specified "no file" for the field
42 // instead of just omitting the field completely. DWTM.
43 $ret = null;
44 } elseif ( !$ret && $this->callbacks->hasParam( $name, $options ) ) {
45 // The client didn't format their upload properly so it came in as an ordinary
46 // field. Convert it to an error.
47 $ret = new UploadedFile( [
48 'name' => '',
49 'type' => '',
50 'tmp_name' => '',
51 'error' => -42, // PHP's UPLOAD_ERR_* are all positive numbers.
52 'size' => 0,
53 ] );
54 }
55
56 return $ret;
57 }
58
59 /**
60 * Fetch the value of PHP's upload_max_filesize ini setting
61 *
62 * This method exists so it can be mocked by unit tests that can't
63 * affect ini_get() directly.
64 *
65 * @codeCoverageIgnore
66 * @return string|false
67 */
68 protected function getIniSize() {
69 return ini_get( 'upload_max_filesize' );
70 }
71
72 public function validate( $name, $value, array $settings, array $options ) {
73 static $codemap = [
74 -42 => 'notupload', // Local from getValue()
75 UPLOAD_ERR_FORM_SIZE => 'formsize',
76 UPLOAD_ERR_PARTIAL => 'partial',
77 UPLOAD_ERR_NO_FILE => 'nofile',
78 UPLOAD_ERR_NO_TMP_DIR => 'notmpdir',
79 UPLOAD_ERR_CANT_WRITE => 'cantwrite',
80 UPLOAD_ERR_EXTENSION => 'phpext',
81 ];
82
83 if ( !$value instanceof UploadedFileInterface ) {
84 // Err?
85 throw new ValidationException( $name, $value, $settings, 'badupload', [] );
86 }
87
88 $err = $value->getError();
89 if ( $err === UPLOAD_ERR_OK ) {
90 return $value;
91 } elseif ( $err === UPLOAD_ERR_INI_SIZE ) {
92 static $prefixes = [
93 'g' => 1024 ** 3,
94 'm' => 1024 ** 2,
95 'k' => 1024 ** 1,
96 ];
97 $size = $this->getIniSize();
98 $last = strtolower( substr( $size, -1 ) );
99 $size = intval( $size, 10 ) * ( $prefixes[$last] ?? 1 );
100 throw new ValidationException( $name, $value, $settings, 'badupload-inisize', [
101 'size' => $size,
102 ] );
103 } elseif ( isset( $codemap[$err] ) ) {
104 throw new ValidationException( $name, $value, $settings, 'badupload-' . $codemap[$err], [] );
105 } else {
106 throw new ValidationException( $name, $value, $settings, 'badupload-unknown', [
107 'code' => $err,
108 ] );
109 }
110 }
111
112 public function stringifyValue( $name, $value, array $settings, array $options ) {
113 // Not going to happen.
114 return null;
115 }
116
117 }