Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
93 / 93 |
|
100.00% |
8 / 8 |
CRAP | |
100.00% |
1 / 1 |
EnumDef | |
100.00% |
93 / 93 |
|
100.00% |
8 / 8 |
29 | |
100.00% |
1 / 1 |
validate | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 | |||
checkSettings | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
9 | |||
getEnumValues | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
stringifyValue | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getParamInfo | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
3 | |||
getHelpInfo | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
3 | |||
sortEnumValues | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
getEnumValuesForHelp | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace Wikimedia\ParamValidator\TypeDef; |
4 | |
5 | use Wikimedia\Message\DataMessageValue; |
6 | use Wikimedia\Message\ListParam; |
7 | use Wikimedia\Message\ListType; |
8 | use Wikimedia\Message\MessageParam; |
9 | use Wikimedia\Message\MessageValue; |
10 | use Wikimedia\Message\ParamType; |
11 | use Wikimedia\Message\ScalarParam; |
12 | use Wikimedia\ParamValidator\ParamValidator; |
13 | use Wikimedia\ParamValidator\TypeDef; |
14 | |
15 | /** |
16 | * Type definition for enumeration types. |
17 | * |
18 | * This class expects that PARAM_TYPE is an array of allowed values. Subclasses |
19 | * may override getEnumValues() to determine the allowed values differently. |
20 | * |
21 | * The result from validate() is one of the defined values. |
22 | * |
23 | * Failure codes: |
24 | * - 'badvalue': The value is not a recognized value. No data. |
25 | * |
26 | * Additional codes may be generated when using certain PARAM constants. See |
27 | * the constants' documentation for details. |
28 | * |
29 | * @since 1.34 |
30 | * @unstable |
31 | */ |
32 | class EnumDef extends TypeDef { |
33 | |
34 | /** |
35 | * (array) Associative array of deprecated values. |
36 | * |
37 | * Keys are the deprecated parameter values. Value is one of the following: |
38 | * - null: Parameter isn't actually deprecated. |
39 | * - true: Parameter is deprecated. |
40 | * - MessageValue: Parameter is deprecated, and this message (converted to a DataMessageValue) |
41 | * is used in place of the default for passing to $this->failure(). |
42 | * |
43 | * Note that this does not add any values to the enumeration, it only |
44 | * documents existing values as being deprecated. |
45 | * |
46 | * Failure codes: (non-fatal) |
47 | * - 'deprecated-value': A deprecated value was encountered. No data. |
48 | */ |
49 | public const PARAM_DEPRECATED_VALUES = 'param-deprecated-values'; |
50 | |
51 | public function validate( $name, $value, array $settings, array $options ) { |
52 | $values = $this->getEnumValues( $name, $settings, $options ); |
53 | |
54 | if ( in_array( $value, $values, true ) ) { |
55 | // Set a warning if a deprecated parameter value has been passed |
56 | if ( empty( $options['is-default'] ) && |
57 | isset( $settings[self::PARAM_DEPRECATED_VALUES][$value] ) |
58 | ) { |
59 | $msg = $settings[self::PARAM_DEPRECATED_VALUES][$value]; |
60 | if ( $msg instanceof MessageValue ) { |
61 | $message = DataMessageValue::new( |
62 | $msg->getKey(), |
63 | $msg->getParams(), |
64 | 'deprecated-value', |
65 | $msg instanceof DataMessageValue ? $msg->getData() : null |
66 | ); |
67 | } else { |
68 | $message = $this->failureMessage( 'deprecated-value' ); |
69 | } |
70 | $this->failure( $message, $name, $value, $settings, $options, false ); |
71 | } |
72 | |
73 | return $value; |
74 | } |
75 | |
76 | $isMulti = isset( $options['values-list'] ); |
77 | $this->failure( |
78 | $this->failureMessage( 'badvalue', [], $isMulti ? 'enummulti' : 'enumnotmulti' ) |
79 | ->textListParams( array_map( static function ( $v ) { |
80 | return new ScalarParam( ParamType::PLAINTEXT, $v ); |
81 | }, $values ) ) |
82 | ->numParams( count( $values ) ), |
83 | $name, $value, $settings, $options |
84 | ); |
85 | } |
86 | |
87 | public function checkSettings( string $name, $settings, array $options, array $ret ): array { |
88 | $ret = parent::checkSettings( $name, $settings, $options, $ret ); |
89 | |
90 | $ret['allowedKeys'][] = self::PARAM_DEPRECATED_VALUES; |
91 | |
92 | $dv = $settings[self::PARAM_DEPRECATED_VALUES] ?? []; |
93 | if ( !is_array( $dv ) ) { |
94 | $ret['issues'][self::PARAM_DEPRECATED_VALUES] = 'PARAM_DEPRECATED_VALUES must be an array, got ' |
95 | . gettype( $dv ); |
96 | } else { |
97 | $values = array_map( function ( $v ) use ( $name, $settings, $options ) { |
98 | return $this->stringifyValue( $name, $v, $settings, $options ); |
99 | }, $this->getEnumValues( $name, $settings, $options ) ); |
100 | foreach ( $dv as $k => $v ) { |
101 | $k = $this->stringifyValue( $name, $k, $settings, $options ); |
102 | if ( !in_array( $k, $values, true ) ) { |
103 | $ret['issues'][] = "PARAM_DEPRECATED_VALUES contains \"$k\", which is not " |
104 | . 'one of the enumerated values'; |
105 | } elseif ( $v instanceof MessageValue ) { |
106 | $ret['messages'][] = $v; |
107 | } elseif ( $v !== null && $v !== true ) { |
108 | $type = $v === false ? 'false' : ( is_object( $v ) ? get_class( $v ) : gettype( $v ) ); |
109 | $ret['issues'][] = 'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, ' |
110 | . "but value for \"$k\" is $type"; |
111 | } |
112 | } |
113 | } |
114 | |
115 | return $ret; |
116 | } |
117 | |
118 | public function getEnumValues( $name, array $settings, array $options ) { |
119 | return array_values( $settings[ParamValidator::PARAM_TYPE] ); |
120 | } |
121 | |
122 | public function stringifyValue( $name, $value, array $settings, array $options ) { |
123 | if ( !is_array( $value ) ) { |
124 | return parent::stringifyValue( $name, $value, $settings, $options ); |
125 | } |
126 | |
127 | return ParamValidator::implodeMultiValue( $value ); |
128 | } |
129 | |
130 | public function getParamInfo( $name, array $settings, array $options ) { |
131 | $info = parent::getParamInfo( $name, $settings, $options ); |
132 | |
133 | $info['type'] = $this->sortEnumValues( |
134 | $name, |
135 | $this->getEnumValues( $name, $settings, $options ), |
136 | $settings, |
137 | $options |
138 | ); |
139 | |
140 | if ( !empty( $settings[self::PARAM_DEPRECATED_VALUES] ) ) { |
141 | $deprecatedValues = array_intersect( |
142 | array_keys( $settings[self::PARAM_DEPRECATED_VALUES] ), |
143 | $this->getEnumValues( $name, $settings, $options ) |
144 | ); |
145 | if ( $deprecatedValues ) { |
146 | $deprecatedValues = $this->sortEnumValues( $name, $deprecatedValues, $settings, $options ); |
147 | $info['deprecatedvalues'] = array_values( $deprecatedValues ); |
148 | } |
149 | } |
150 | |
151 | return $info; |
152 | } |
153 | |
154 | public function getHelpInfo( $name, array $settings, array $options ) { |
155 | $info = parent::getHelpInfo( $name, $settings, $options ); |
156 | |
157 | $isMulti = !empty( $settings[ParamValidator::PARAM_ISMULTI] ); |
158 | |
159 | $values = $this->getEnumValuesForHelp( $name, $settings, $options ); |
160 | $count = count( $values ); |
161 | |
162 | $i = array_search( '', $values, true ); |
163 | if ( $i === false ) { |
164 | $valuesParam = new ListParam( ListType::COMMA, $values ); |
165 | } else { |
166 | unset( $values[$i] ); |
167 | $valuesParam = MessageValue::new( 'paramvalidator-help-type-enum-can-be-empty' ) |
168 | ->commaListParams( $values ) |
169 | ->numParams( count( $values ) ); |
170 | } |
171 | |
172 | $info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-enum' ) |
173 | ->params( $isMulti ? 2 : 1 ) |
174 | ->params( $valuesParam ) |
175 | ->numParams( $count ); |
176 | |
177 | // Suppress standard ISMULTI message, it should be incorporated into our type message. |
178 | $info[ParamValidator::PARAM_ISMULTI] = null; |
179 | |
180 | return $info; |
181 | } |
182 | |
183 | /** |
184 | * Sort enum values for help/param info output |
185 | * |
186 | * @param string $name Parameter name being described. |
187 | * @param string[] $values Values being sorted |
188 | * @param array $settings Parameter settings array. |
189 | * @param array $options Options array. |
190 | * @return string[] |
191 | */ |
192 | protected function sortEnumValues( |
193 | string $name, array $values, array $settings, array $options |
194 | ): array { |
195 | // sort values by deprecation status and name |
196 | $flags = []; |
197 | foreach ( $values as $k => $value ) { |
198 | $flag = 0; |
199 | if ( isset( $settings[self::PARAM_DEPRECATED_VALUES][$value] ) ) { |
200 | $flag |= 1; |
201 | } |
202 | $flags[$k] = $flag; |
203 | } |
204 | array_multisort( $flags, $values, SORT_NATURAL ); |
205 | |
206 | return $values; |
207 | } |
208 | |
209 | /** |
210 | * Return enum values formatted for the help message |
211 | * |
212 | * @param string $name Parameter name being described. |
213 | * @param array $settings Parameter settings array. |
214 | * @param array $options Options array. |
215 | * @return (MessageParam|string)[] |
216 | */ |
217 | protected function getEnumValuesForHelp( $name, array $settings, array $options ) { |
218 | $values = $this->getEnumValues( $name, $settings, $options ); |
219 | $values = $this->sortEnumValues( $name, $values, $settings, $options ); |
220 | |
221 | // @todo Indicate deprecated values in some manner. Probably that needs |
222 | // MessageValue and/or MessageParam to have a generic ability to wrap |
223 | // values in HTML without that HTML coming out in the text format too. |
224 | |
225 | return $values; |
226 | } |
227 | |
228 | } |