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 ( !is_bool( $settings[ApiBase::PARAM_RANGE_ENFORCE] ?? false ) ) {
210  $ret['issues'][ApiBase::PARAM_RANGE_ENFORCE] = 'PARAM_RANGE_ENFORCE must be boolean, got '
211  . gettype( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
212  }
213 
214  if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
215  $this->checkSettingsMessage(
216  $module, 'PARAM_HELP_MSG', $settings[ApiBase::PARAM_HELP_MSG], $ret
217  );
218  }
219 
220  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
221  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
222  $ret['issues'][ApiBase::PARAM_HELP_MSG_APPEND] = 'PARAM_HELP_MSG_APPEND must be an array, got '
223  . gettype( $settings[ApiBase::PARAM_HELP_MSG_APPEND] );
224  } else {
225  foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $k => $v ) {
226  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_APPEND[$k]", $v, $ret );
227  }
228  }
229  }
230 
231  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
232  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
233  $ret['issues'][ApiBase::PARAM_HELP_MSG_INFO] = 'PARAM_HELP_MSG_INFO must be an array, got '
234  . gettype( $settings[ApiBase::PARAM_HELP_MSG_INFO] );
235  } else {
236  $path = $module->getModulePath();
237  foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $k => $v ) {
238  if ( !is_array( $v ) ) {
239  $ret['issues'][] = "PARAM_HELP_MSG_INFO[$k] must be an array, got " . gettype( $v );
240  } elseif ( !is_string( $v[0] ) ) {
241  $ret['issues'][] = "PARAM_HELP_MSG_INFO[$k][0] must be a string, got " . gettype( $v[0] );
242  } else {
243  $v[0] = "apihelp-{$path}-paraminfo-{$v[0]}";
244  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_INFO[$k]", $v, $ret );
245  }
246  }
247  }
248  }
249 
250  if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
251  if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
252  $ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE must be an array,'
253  . ' got ' . gettype( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] );
254  } elseif ( !is_array( $settings[ParamValidator::PARAM_TYPE] ?? '' ) ) {
255  $ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE can only be used '
256  . 'with PARAM_TYPE as an array';
257  } else {
258  $values = array_map( 'strval', $settings[ParamValidator::PARAM_TYPE] );
259  foreach ( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] as $k => $v ) {
260  if ( !in_array( (string)$k, $values, true ) ) {
261  // Or should this be allowed?
262  $ret['issues'][] = "PARAM_HELP_MSG_PER_VALUE contains \"$k\", which is not in PARAM_TYPE.";
263  }
264  $this->checkSettingsMessage( $module, "PARAM_HELP_MSG_PER_VALUE[$k]", $v, $ret );
265  }
266  }
267  }
268 
269  if ( isset( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
270  if ( !is_array( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
271  $ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS must be an array,'
272  . ' got ' . gettype( $settings[ApiBase::PARAM_TEMPLATE_VARS] );
273  } elseif ( $settings[ApiBase::PARAM_TEMPLATE_VARS] === [] ) {
274  $ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS cannot be the empty array';
275  } else {
276  foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $key => $target ) {
277  if ( !preg_match( '/^[^{}]+$/', $key ) ) {
278  $ret['issues'][] = "PARAM_TEMPLATE_VARS keys may not contain '{' or '}', got \"$key\"";
279  } elseif ( strpos( $name, '{' . $key . '}' ) === false ) {
280  $ret['issues'][] = "Parameter name must contain PARAM_TEMPLATE_VARS key {{$key}}";
281  }
282  if ( !is_string( $target ) && !is_int( $target ) ) {
283  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] has invalid target type " . gettype( $target );
284  } elseif ( !isset( $params[$target] ) ) {
285  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" does not exist";
286  } else {
287  $settings2 = $params[$target];
288  if ( empty( $settings2[ParamValidator::PARAM_ISMULTI] ) ) {
289  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" must have "
290  . 'PARAM_ISMULTI = true';
291  }
292  if ( isset( $settings2[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
293  if ( $target === $name ) {
294  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] cannot target the parameter itself";
295  }
296  if ( array_diff(
297  $settings2[ApiBase::PARAM_TEMPLATE_VARS],
299  ) ) {
300  $ret['issues'][] = "PARAM_TEMPLATE_VARS[$key]: Target's "
301  . 'PARAM_TEMPLATE_VARS must be a subset of the original';
302  }
303  }
304  }
305  }
306 
307  $keys = implode( '|', array_map(
308  static function ( $key ) {
309  return preg_quote( $key, '/' );
310  },
311  array_keys( $settings[ApiBase::PARAM_TEMPLATE_VARS] )
312  ) );
313  if ( !preg_match( '/^(?>[^{}]+|\{(?:' . $keys . ')\})+$/', $name ) ) {
314  $ret['issues'][] = "Parameter name may not contain '{' or '}' other than '
315  . 'as defined by PARAM_TEMPLATE_VARS";
316  }
317  }
318  } elseif ( !preg_match( '/^[^{}]+$/', $name ) ) {
319  $ret['issues'][] = "Parameter name may not contain '{' or '}' without PARAM_TEMPLATE_VARS";
320  }
321 
322  return $ret;
323  }
324 
332  private function convertValidationException( ApiBase $module, ValidationException $ex ) {
333  $mv = $ex->getFailureMessage();
335  $module,
336  $this->messageConverter->convertMessageValue( $mv ),
337  $mv->getCode(),
338  $mv->getData(),
339  0,
340  $ex
341  );
342  }
343 
354  public function getValue( ApiBase $module, string $name, $settings, array $options = [] ) {
355  $options['module'] = $module;
356  $name = $module->encodeParamName( $name );
357  $settings = $this->normalizeSettings( $settings );
358  try {
359  return $this->paramValidator->getValue( $name, $settings, $options );
360  } catch ( ValidationException $ex ) {
361  $this->convertValidationException( $module, $ex );
362  }
363  }
364 
377  public function validateValue(
378  ApiBase $module, string $name, $value, $settings, array $options = []
379  ) {
380  $options['module'] = $module;
381  $name = $module->encodeParamName( $name );
382  $settings = $this->normalizeSettings( $settings );
383  try {
384  return $this->paramValidator->validateValue( $name, $value, $settings, $options );
385  } catch ( ValidationException $ex ) {
386  $this->convertValidationException( $module, $ex );
387  }
388  }
389 
400  public function getParamInfo( ApiBase $module, string $name, $settings, array $options ): array {
401  $options['module'] = $module;
402  $name = $module->encodeParamName( $name );
403  return $this->paramValidator->getParamInfo( $name, $settings, $options );
404  }
405 
416  public function getHelpInfo( ApiBase $module, string $name, $settings, array $options ): array {
417  $options['module'] = $module;
418  $name = $module->encodeParamName( $name );
419 
420  $ret = $this->paramValidator->getHelpInfo( $name, $settings, $options );
421  foreach ( $ret as &$m ) {
422  $k = $m->getKey();
423  $m = $this->messageConverter->convertMessageValue( $m );
424  if ( substr( $k, 0, 20 ) === 'paramvalidator-help-' ) {
425  $m = new Message(
426  [ 'api-help-param-' . substr( $k, 20 ), $k ],
427  $m->getParams()
428  );
429  }
430  }
431  '@phan-var Message[] $ret'; // The above loop converts it
432 
433  return $ret;
434  }
435 }
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:416
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:162
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:1216
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:169
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:18
MediaWiki\Api\Validator\ApiParamValidator\getValue
getValue(ApiBase $module, string $name, $settings, array $options=[])
Get and validate a value.
Definition: ApiParamValidator.php:354
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.
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:400
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:572
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:226
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:742
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:179
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:152
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:213
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:139
MediaWiki\Api\Validator\ApiParamValidator\convertValidationException
convertValidationException(ApiBase $module, ValidationException $ex)
Convert a ValidationException to an ApiUsageException.
Definition: ApiParamValidator.php:332
$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:195
MediaWiki\Api\Validator\ApiParamValidator\validateValue
validateValue(ApiBase $module, string $name, $value, $settings, array $options=[])
Validate a parameter value using a settings array.
Definition: ApiParamValidator.php:377
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:224