MediaWiki  master
ApiParamValidator.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use ApiBase;
6 use ApiMain;
7 use ApiMessage;
9 use MediaWiki\Message\Converter as MessageConverter;
14 use Message;
17 use Wikimedia\ObjectFactory;
29 
39 
41  private $paramValidator;
42 
45 
47  private const TYPE_DEFS = [
48  'boolean' => [ 'class' => PresenceBooleanDef::class ],
49  'enum' => [ 'class' => EnumDef::class ],
50  'expiry' => [ 'class' => ExpiryDef::class ],
51  'integer' => [ 'class' => IntegerDef::class ],
52  'limit' => [ 'class' => LimitDef::class ],
53  'namespace' => [
54  'class' => NamespaceDef::class,
55  'services' => [ 'NamespaceInfo' ],
56  ],
57  'NULL' => [
58  'class' => StringDef::class,
59  'args' => [ [
60  'allowEmptyWhenRequired' => true,
61  ] ],
62  ],
63  'password' => [ 'class' => PasswordDef::class ],
64  // Unlike 'string', the 'raw' type will not be subject to Unicode
65  // NFC normalization.
66  'raw' => [ 'class' => StringDef::class ],
67  'string' => [ 'class' => StringDef::class ],
68  'submodule' => [ 'class' => SubmoduleDef::class ],
69  'tags' => [ 'class' => TagsDef::class ],
70  'text' => [ 'class' => StringDef::class ],
71  'timestamp' => [
72  'class' => TimestampDef::class,
73  'args' => [ [
74  'defaultFormat' => TS_MW,
75  ] ],
76  ],
77  'title' => [
78  'class' => TitleDef::class,
79  'services' => [ 'TitleFactory' ],
80  ],
81  'user' => [
82  'class' => UserDef::class,
83  'services' => [ 'UserIdentityLookup', 'TitleParser', 'UserNameUtils' ]
84  ],
85  'upload' => [ 'class' => UploadDef::class ],
86  ];
87 
93  public function __construct( ApiMain $main, ObjectFactory $objectFactory ) {
94  $this->paramValidator = new ParamValidator(
95  new ApiParamValidatorCallbacks( $main ),
96  $objectFactory,
97  [
98  'typeDefs' => self::TYPE_DEFS,
99  'ismultiLimits' => [ ApiBase::LIMIT_SML1, ApiBase::LIMIT_SML2 ],
100  ]
101  );
102  $this->messageConverter = new MessageConverter();
103  }
104 
109  public function knownTypes(): array {
110  return $this->paramValidator->knownTypes();
111  }
112 
118  private function mapDeprecatedSettingsMessages( array $settings ): array {
119  if ( isset( $settings[EnumDef::PARAM_DEPRECATED_VALUES] ) ) {
120  foreach ( $settings[EnumDef::PARAM_DEPRECATED_VALUES] as &$v ) {
121  if ( $v === null || $v === true || $v instanceof MessageValue ) {
122  continue;
123  }
124 
125  // Convert the message specification to a DataMessageValue. Flag in the data
126  // that it was so converted, so ApiParamValidatorCallbacks::recordCondition() can
127  // take that into account.
128  // @phan-suppress-next-line PhanTypeMismatchArgument
129  $msg = $this->messageConverter->convertMessage( ApiMessage::create( $v ) );
131  $msg->getKey(),
132  $msg->getParams(),
133  'bogus',
134  [ '💩' => 'back-compat' ]
135  );
136  }
137  unset( $v );
138  }
139 
140  return $settings;
141  }
142 
148  public function normalizeSettings( $settings ): array {
149  if ( is_array( $settings ) ) {
150  if ( !isset( $settings[ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES] ) ) {
152  }
153 
154  if ( !isset( $settings[IntegerDef::PARAM_IGNORE_RANGE] ) ) {
155  $settings[IntegerDef::PARAM_IGNORE_RANGE] = empty( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
156  }
157 
158  $settings = $this->mapDeprecatedSettingsMessages( $settings );
159  }
160 
161  return $this->paramValidator->normalizeSettings( $settings );
162  }
163 
171  private function checkSettingsMessage( ApiBase $module, string $key, $value, array &$ret ): void {
172  $msg = ApiBase::makeMessage( $value, $module );
173  if ( $msg instanceof Message ) {
174  $ret['messages'][] = $this->messageConverter->convertMessage( $msg );
175  } else {
176  $ret['issues'][] = "Message specification for $key is not valid";
177  }
178  }
179 
188  public function checkSettings(
189  ApiBase $module, array $params, string $name, array $options
190  ): array {
191  $options['module'] = $module;
192  $settings = $params[$name];
193  if ( is_array( $settings ) ) {
194  $settings = $this->mapDeprecatedSettingsMessages( $settings );
195  }
196  $ret = $this->paramValidator->checkSettings(
197  $module->encodeParamName( $name ), $settings, $options
198  );
199 
200  $ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
203  ] );
204 
205  if ( !is_array( $settings ) ) {
206  $settings = [];
207  }
208 
209  if ( array_key_exists( ApiBase::PARAM_VALUE_LINKS, $settings ) ) {
210  $ret['issues'][ApiBase::PARAM_VALUE_LINKS]
211  = 'PARAM_VALUE_LINKS was deprecated in MediaWiki 1.35';
212  }
213 
214  if ( !is_bool( $settings[ApiBase::PARAM_RANGE_ENFORCE] ?? false ) ) {
215  $ret['issues'][ApiBase::PARAM_RANGE_ENFORCE] = 'PARAM_RANGE_ENFORCE must be boolean, got '
216  . gettype( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
217  }
218 
219  if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
220  $this->checkSettingsMessage(
221  $module, 'PARAM_HELP_MSG', $settings[ApiBase::PARAM_HELP_MSG], $ret
222  );
223  }
224 
225  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
226  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
227  $ret['issues'][ApiBase::PARAM_HELP_MSG_APPEND] = 'PARAM_HELP_MSG_APPEND must be an array, got '
228  . gettype( $settings[ApiBase::PARAM_HELP_MSG_APPEND] );
229  } else {
230  foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $k => $v ) {
231  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_APPEND[$k]", $v, $ret );
232  }
233  }
234  }
235 
236  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
237  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
238  $ret['issues'][ApiBase::PARAM_HELP_MSG_INFO] = 'PARAM_HELP_MSG_INFO must be an array, got '
239  . gettype( $settings[ApiBase::PARAM_HELP_MSG_INFO] );
240  } else {
241  $path = $module->getModulePath();
242  foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $k => $v ) {
243  if ( !is_array( $v ) ) {
244  $ret['issues'][] = "PARAM_HELP_MSG_INFO[$k] must be an array, got " . gettype( $v );
245  } elseif ( !is_string( $v[0] ) ) {
246  $ret['issues'][] = "PARAM_HELP_MSG_INFO[$k][0] must be a string, got " . gettype( $v[0] );
247  } else {
248  $v[0] = "apihelp-{$path}-paraminfo-{$v[0]}";
249  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_INFO[$k]", $v, $ret );
250  }
251  }
252  }
253  }
254 
255  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
256  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
257  $ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE must be an array,'
258  . ' got ' . gettype( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] );
259  } elseif ( !is_array( $settings[ParamValidator::PARAM_TYPE] ?? '' ) ) {
260  $ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE can only be used '
261  . 'with PARAM_TYPE as an array';
262  } else {
263  $values = array_map( 'strval', $settings[ParamValidator::PARAM_TYPE] );
264  foreach ( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] as $k => $v ) {
265  if ( !in_array( (string)$k, $values, true ) ) {
266  // Or should this be allowed?
267  $ret['issues'][] = "PARAM_HELP_MSG_PER_VALUE contains \"$k\", which is not in PARAM_TYPE.";
268  }
269  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_PER_VALUE[$k]", $v, $ret );
270  }
271  }
272  }
273 
274  if ( isset( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
275  if ( !is_array( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
276  $ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS must be an array,'
277  . ' got ' . gettype( $settings[ApiBase::PARAM_TEMPLATE_VARS] );
278  } elseif ( $settings[ApiBase::PARAM_TEMPLATE_VARS] === [] ) {
279  $ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS cannot be the empty array';
280  } else {
281  foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $key => $target ) {
282  if ( !preg_match( '/^[^{}]+$/', $key ) ) {
283  $ret['issues'][] = "PARAM_TEMPLATE_VARS keys may not contain '{' or '}', got \"$key\"";
284  } elseif ( strpos( $name, '{' . $key . '}' ) === false ) {
285  $ret['issues'][] = "Parameter name must contain PARAM_TEMPLATE_VARS key {{$key}}";
286  }
287  if ( !is_string( $target ) && !is_int( $target ) ) {
288  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] has invalid target type " . gettype( $target );
289  } elseif ( !isset( $params[$target] ) ) {
290  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" does not exist";
291  } else {
292  $settings2 = $params[$target];
293  if ( empty( $settings2[ParamValidator::PARAM_ISMULTI] ) ) {
294  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" must have "
295  . 'PARAM_ISMULTI = true';
296  }
297  if ( isset( $settings2[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
298  if ( $target === $name ) {
299  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] cannot target the parameter itself";
300  }
301  if ( array_diff(
302  $settings2[ApiBase::PARAM_TEMPLATE_VARS],
304  ) ) {
305  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key]: Target's "
306  . 'PARAM_TEMPLATE_VARS must be a subset of the original';
307  }
308  }
309  }
310  }
311 
312  $keys = implode( '|', array_map(
313  static function ( $key ) {
314  return preg_quote( $key, '/' );
315  },
316  array_keys( $settings[ApiBase::PARAM_TEMPLATE_VARS] )
317  ) );
318  if ( !preg_match( '/^(?>[^{}]+|\{(?:' . $keys . ')\})+$/', $name ) ) {
319  $ret['issues'][] = "Parameter name may not contain '{' or '}' other than '
320  . 'as defined by PARAM_TEMPLATE_VARS";
321  }
322  }
323  } elseif ( !preg_match( '/^[^{}]+$/', $name ) ) {
324  $ret['issues'][] = "Parameter name may not contain '{' or '}' without PARAM_TEMPLATE_VARS";
325  }
326 
327  return $ret;
328  }
329 
337  private function convertValidationException( ApiBase $module, ValidationException $ex ) {
338  $mv = $ex->getFailureMessage();
340  $module,
341  $this->messageConverter->convertMessageValue( $mv ),
342  $mv->getCode(),
343  $mv->getData(),
344  0,
345  $ex
346  );
347  }
348 
359  public function getValue( ApiBase $module, string $name, $settings, array $options = [] ) {
360  $options['module'] = $module;
361  $name = $module->encodeParamName( $name );
362  $settings = $this->normalizeSettings( $settings );
363  try {
364  return $this->paramValidator->getValue( $name, $settings, $options );
365  } catch ( ValidationException $ex ) {
366  $this->convertValidationException( $module, $ex );
367  }
368  }
369 
382  public function validateValue(
383  ApiBase $module, string $name, $value, $settings, array $options = []
384  ) {
385  $options['module'] = $module;
386  $name = $module->encodeParamName( $name );
387  $settings = $this->normalizeSettings( $settings );
388  try {
389  return $this->paramValidator->validateValue( $name, $value, $settings, $options );
390  } catch ( ValidationException $ex ) {
391  $this->convertValidationException( $module, $ex );
392  }
393  }
394 
405  public function getParamInfo( ApiBase $module, string $name, $settings, array $options ): array {
406  $options['module'] = $module;
407  $name = $module->encodeParamName( $name );
408  return $this->paramValidator->getParamInfo( $name, $settings, $options );
409  }
410 
421  public function getHelpInfo( ApiBase $module, string $name, $settings, array $options ): array {
422  $options['module'] = $module;
423  $name = $module->encodeParamName( $name );
424 
425  $ret = $this->paramValidator->getHelpInfo( $name, $settings, $options );
426  foreach ( $ret as &$m ) {
427  $k = $m->getKey();
428  $m = $this->messageConverter->convertMessageValue( $m );
429  if ( substr( $k, 0, 20 ) === 'paramvalidator-help-' ) {
430  $m = new Message(
431  [ 'api-help-param-' . substr( $k, 20 ), $k ],
432  $m->getParams()
433  );
434  }
435  }
436  '@phan-var Message[] $ret'; // The above loop converts it
437 
438  return $ret;
439  }
440 }
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:49
MediaWiki\Api\Validator\ApiParamValidator\$messageConverter
MessageConverter $messageConverter
Definition: ApiParamValidator.php:44
MediaWiki\Api\Validator\ApiParamValidator\$paramValidator
ParamValidator $paramValidator
Definition: ApiParamValidator.php:41
ApiUsageException
Exception used to abort API execution with an error.
Definition: ApiUsageException.php:29
ApiUsageException\newWithMessage
static newWithMessage(?ApiBase $module, $msg, $code=null, $data=null, $httpCode=0, Throwable $previous=null)
Definition: ApiUsageException.php:68
Wikimedia\ParamValidator\ValidationException
Error reporting for ParamValidator.
Definition: ValidationException.php:16
MediaWiki\Api\Validator\ApiParamValidator\TYPE_DEFS
const TYPE_DEFS
Type defs for ParamValidator.
Definition: ApiParamValidator.php:47
MediaWiki\Api\Validator\ApiParamValidator\getHelpInfo
getHelpInfo(ApiBase $module, string $name, $settings, array $options)
Describe parameter settings in human-readable format.
Definition: ApiParamValidator.php:421
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:105
MediaWiki\Api\Validator\ApiParamValidator\mapDeprecatedSettingsMessages
mapDeprecatedSettingsMessages(array $settings)
Map deprecated styles for messages for ParamValidator.
Definition: ApiParamValidator.php:118
Wikimedia\ParamValidator\ParamValidator\PARAM_IGNORE_UNRECOGNIZED_VALUES
const PARAM_IGNORE_UNRECOGNIZED_VALUES
(bool) Whether to downgrade "badvalue" errors to non-fatal when validating multi-valued parameters.
Definition: ParamValidator.php:168
MediaWiki\Api\Validator\ApiParamValidator
This wraps a bunch of the API-specific parameter validation logic.
Definition: ApiParamValidator.php:38
Wikimedia\ParamValidator\TypeDef\EnumDef
Type definition for enumeration types.
Definition: EnumDef.php:32
ApiBase\makeMessage
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1159
ApiBase\PARAM_HELP_MSG_APPEND
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:112
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:16
MediaWiki\Api\Validator\ApiParamValidator\getValue
getValue(ApiBase $module, string $name, $settings, array $options=[])
Get and validate a value.
Definition: ApiParamValidator.php:359
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:55
ApiMessage
Extension of Message implementing IApiMessage @newable.
Definition: ApiMessage.php:27
Wikimedia\ParamValidator\ParamValidator::TypeDef\UserDef
Type definition for user types.
Definition: UserDef.php:26
MediaWiki\Api\Validator\ApiParamValidator\getParamInfo
getParamInfo(ApiBase $module, string $name, $settings, array $options)
Describe parameter settings in a machine-readable format.
Definition: ApiParamValidator.php:405
MediaWiki\Api\Validator\ApiParamValidator\checkSettingsMessage
checkSettingsMessage(ApiBase $module, string $key, $value, array &$ret)
Check an API settings message.
Definition: ApiParamValidator.php:171
MediaWiki\Api\Validator\ApiParamValidator\checkSettings
checkSettings(ApiBase $module, array $params, string $name, array $options)
Check settings for the Action API.
Definition: ApiParamValidator.php:188
Wikimedia\ParamValidator\TypeDef\ExpiryDef
Type definition for expiry timestamps.
Definition: ExpiryDef.php:17
Wikimedia\ParamValidator\ValidationException\getFailureMessage
getFailureMessage()
Fetch the validation failure message.
Definition: ValidationException.php:63
ApiBase\getModulePath
getModulePath()
Get the path to this module.
Definition: ApiBase.php:515
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
Wikimedia\ParamValidator\TypeDef\StringDef
Type definition for string types.
Definition: StringDef.php:24
Message\Converter
Converter between Message and MessageValue.
Definition: Converter.php:18
Wikimedia\ParamValidator\TypeDef\TimestampDef
Type definition for timestamp types.
Definition: TimestampDef.php:32
Wikimedia\ParamValidator\ParamValidator\PARAM_ISMULTI
const PARAM_ISMULTI
(bool) Indicate that the parameter is multi-valued.
Definition: ParamValidator.php:112
ApiBase\LIMIT_SML2
const LIMIT_SML2
Slow query, apihighlimits limit.
Definition: ApiBase.php:169
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:685
Wikimedia\Message\DataMessageValue
Value object representing a message for i18n with alternative machine-readable data.
Definition: DataMessageValue.php:23
MediaWiki\Api\Validator\ApiParamValidatorCallbacks
ParamValidator callbacks for the Action API.
Definition: ApiParamValidatorCallbacks.php:16
MediaWiki\Api\Validator\ApiParamValidator\normalizeSettings
normalizeSettings( $settings)
Adjust certain settings where ParamValidator differs from historical Action API behavior.
Definition: ApiParamValidator.php:148
ApiBase\PARAM_HELP_MSG_INFO
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:122
Wikimedia\Message\DataMessageValue\new
static new( $key, $params=[], $code=null, array $data=null)
Static constructor for easier chaining of ->params() methods.
Definition: DataMessageValue.php:55
Wikimedia\ParamValidator\TypeDef\PasswordDef
Type definition for "password" types.
Definition: PasswordDef.php:16
MediaWiki\Api\Validator
Definition: ApiParamValidator.php:3
ApiBase\PARAM_RANGE_ENFORCE
const PARAM_RANGE_ENFORCE
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:95
ApiBase\PARAM_VALUE_LINKS
const PARAM_VALUE_LINKS
Deprecated and unused.
Definition: ApiBase.php:129
Wikimedia\ParamValidator\TypeDef\PresenceBooleanDef
Type definition for checkbox-like boolean types.
Definition: PresenceBooleanDef.php:21
Wikimedia\ParamValidator\TypeDef\LimitDef
Type definition for "limit" types.
Definition: LimitDef.php:18
Wikimedia\ParamValidator\TypeDef\IntegerDef
Type definition for integer types.
Definition: IntegerDef.php:23
Wikimedia\ParamValidator\ParamValidator::TypeDef\TitleDef
Type definition for page titles.
Definition: TitleDef.php:22
ApiBase\PARAM_TEMPLATE_VARS
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition: ApiBase.php:156
Wikimedia\ParamValidator\ParamValidator::TypeDef\NamespaceDef
Type definition for namespace types.
Definition: NamespaceDef.php:18
MediaWiki\Api\Validator\ApiParamValidator\knownTypes
knownTypes()
List known type names.
Definition: ApiParamValidator.php:109
Wikimedia\ParamValidator\TypeDef\EnumDef\PARAM_DEPRECATED_VALUES
const PARAM_DEPRECATED_VALUES
(array) Associative array of deprecated values.
Definition: EnumDef.php:49
Wikimedia\ParamValidator\TypeDef\UploadDef
Type definition for upload types.
Definition: UploadDef.php:34
$path
$path
Definition: NoLocalSettings.php:25
Message
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition: Message.php:138
MediaWiki\Api\Validator\ApiParamValidator\convertValidationException
convertValidationException(ApiBase $module, ValidationException $ex)
Convert a ValidationException to an ApiUsageException.
Definition: ApiParamValidator.php:337
$keys
$keys
Definition: testCompression.php:72
Wikimedia\ParamValidator\TypeDef\NumericDef\PARAM_IGNORE_RANGE
const PARAM_IGNORE_RANGE
(bool) Whether to enforce the specified range.
Definition: NumericDef.php:33
MediaWiki\Api\Validator\ApiParamValidator\__construct
__construct(ApiMain $main, ObjectFactory $objectFactory)
Definition: ApiParamValidator.php:93
Wikimedia\ParamValidator\ParamValidator::TypeDef\TagsDef
Type definition for tags type.
Definition: TagsDef.php:23
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:138
MediaWiki\Api\Validator\ApiParamValidator\validateValue
validateValue(ApiBase $module, string $name, $value, $settings, array $options=[])
Valiate a parameter value using a settings array.
Definition: ApiParamValidator.php:382
Wikimedia\ParamValidator\ParamValidator\PARAM_TYPE
const PARAM_TYPE
(string|array) Type of the parameter.
Definition: ParamValidator.php:76
Wikimedia\ParamValidator\ParamValidator
Service for formatting and validating API parameters.
Definition: ParamValidator.php:42
ApiBase\LIMIT_SML1
const LIMIT_SML1
Slow query, standard limit.
Definition: ApiBase.php:167