MediaWiki  master
ApiParamInfo.php
Go to the documentation of this file.
1 <?php
27 
31 class ApiParamInfo extends ApiBase {
32 
33  private $helpFormat;
34 
36  private $context;
37 
39  private $userFactory;
40 
46  public function __construct(
47  ApiMain $main,
48  $action,
49  UserFactory $userFactory
50  ) {
51  parent::__construct( $main, $action );
52  $this->userFactory = $userFactory;
53  }
54 
55  public function execute() {
56  // Get parameters
57  $params = $this->extractRequestParams();
58 
59  $this->helpFormat = $params['helpformat'];
60  $this->context = new RequestContext;
61  $this->context->setUser( $this->userFactory->newAnonymous() ); // anon to avoid caching issues
62  $this->context->setLanguage( $this->getMain()->getLanguage() );
63 
64  if ( is_array( $params['modules'] ) ) {
65  $modules = [];
66  foreach ( $params['modules'] as $path ) {
67  if ( $path === '*' || $path === '**' ) {
68  $path = "main+$path";
69  }
70  if ( str_ends_with( $path, '+*' ) || str_ends_with( $path, ' *' ) ) {
71  $submodules = true;
72  $path = substr( $path, 0, -2 );
73  $recursive = false;
74  } elseif ( str_ends_with( $path, '+**' ) || str_ends_with( $path, ' **' ) ) {
75  $submodules = true;
76  $path = substr( $path, 0, -3 );
77  $recursive = true;
78  } else {
79  $submodules = false;
80  }
81 
82  if ( $submodules ) {
83  try {
84  $module = $this->getModuleFromPath( $path );
85  } catch ( ApiUsageException $ex ) {
86  foreach ( $ex->getStatusValue()->getErrors() as $error ) {
87  $this->addWarning( $error );
88  }
89  continue;
90  }
91  // @phan-suppress-next-next-line PhanTypeMismatchArgumentNullable,PhanPossiblyUndeclaredVariable
92  // recursive is set when used
93  $submodules = $this->listAllSubmodules( $module, $recursive );
94  if ( $submodules ) {
95  $modules = array_merge( $modules, $submodules );
96  } else {
97  $this->addWarning( [ 'apierror-badmodule-nosubmodules', $path ], 'badmodule' );
98  }
99  } else {
100  $modules[] = $path;
101  }
102  }
103  } else {
104  $modules = [];
105  }
106 
107  if ( is_array( $params['querymodules'] ) ) {
108  $queryModules = $params['querymodules'];
109  foreach ( $queryModules as $m ) {
110  $modules[] = 'query+' . $m;
111  }
112  } else {
113  $queryModules = [];
114  }
115 
116  if ( is_array( $params['formatmodules'] ) ) {
117  $formatModules = $params['formatmodules'];
118  foreach ( $formatModules as $m ) {
119  $modules[] = $m;
120  }
121  } else {
122  $formatModules = [];
123  }
124 
125  $modules = array_unique( $modules );
126 
127  $res = [];
128 
129  foreach ( $modules as $m ) {
130  try {
131  $module = $this->getModuleFromPath( $m );
132  } catch ( ApiUsageException $ex ) {
133  foreach ( $ex->getStatusValue()->getErrors() as $error ) {
134  $this->addWarning( $error );
135  }
136  continue;
137  }
138  $key = 'modules';
139 
140  // Back compat
141  $isBCQuery = false;
142  if ( $module->getParent() && $module->getParent()->getModuleName() == 'query' &&
143  in_array( $module->getModuleName(), $queryModules )
144  ) {
145  $isBCQuery = true;
146  $key = 'querymodules';
147  }
148  if ( in_array( $module->getModuleName(), $formatModules ) ) {
149  $key = 'formatmodules';
150  }
151 
152  $item = $this->getModuleInfo( $module );
153  if ( $isBCQuery ) {
154  $item['querytype'] = $item['group'];
155  }
156  $res[$key][] = $item;
157  }
158 
159  $result = $this->getResult();
160  $result->addValue( [ $this->getModuleName() ], 'helpformat', $this->helpFormat );
161 
162  foreach ( $res as $key => $stuff ) {
163  ApiResult::setIndexedTagName( $res[$key], 'module' );
164  }
165 
166  if ( $params['mainmodule'] ) {
167  $res['mainmodule'] = $this->getModuleInfo( $this->getMain() );
168  }
169 
170  if ( $params['pagesetmodule'] ) {
171  $pageSet = new ApiPageSet( $this->getMain()->getModuleManager()->getModule( 'query' ) );
172  $res['pagesetmodule'] = $this->getModuleInfo( $pageSet );
173  unset( $res['pagesetmodule']['name'] );
174  unset( $res['pagesetmodule']['path'] );
175  unset( $res['pagesetmodule']['group'] );
176  }
177 
178  $result->addValue( null, $this->getModuleName(), $res );
179  }
180 
187  private function listAllSubmodules( ApiBase $module, $recursive ) {
188  $paths = [];
189  $manager = $module->getModuleManager();
190  if ( $manager ) {
191  $names = $manager->getNames();
192  sort( $names );
193  foreach ( $names as $name ) {
194  $submodule = $manager->getModule( $name );
195  $paths[] = $submodule->getModulePath();
196  if ( $recursive && $submodule->getModuleManager() ) {
197  $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) );
198  }
199  }
200  }
201  return $paths;
202  }
203 
210  protected function formatHelpMessages( array &$res, $key, array $msgs, $joinLists = false ) {
211  switch ( $this->helpFormat ) {
212  case 'none':
213  break;
214 
215  case 'wikitext':
216  $ret = [];
217  foreach ( $msgs as $m ) {
218  $ret[] = $m->setContext( $this->context )->text();
219  }
220  $res[$key] = implode( "\n\n", $ret );
221  if ( $joinLists ) {
222  $res[$key] = preg_replace( '!^(([*#:;])[^\n]*)\n\n(?=\2)!m', "$1\n", $res[$key] );
223  }
224  break;
225 
226  case 'html':
227  $ret = [];
228  foreach ( $msgs as $m ) {
229  $ret[] = $m->setContext( $this->context )->parseAsBlock();
230  }
231  $ret = implode( "\n", $ret );
232  if ( $joinLists ) {
233  $ret = preg_replace( '!\s*</([oud]l)>\s*<\1>\s*!', "\n", $ret );
234  }
235  $res[$key] = Parser::stripOuterParagraph( $ret );
236  break;
237 
238  case 'raw':
239  $res[$key] = [];
240  foreach ( $msgs as $m ) {
241  $a = [
242  'key' => $m->getKey(),
243  'params' => $m->getParams(),
244  ];
245  ApiResult::setIndexedTagName( $a['params'], 'param' );
246  if ( $m instanceof ApiHelpParamValueMessage ) {
247  $a['forvalue'] = $m->getParamValue();
248  }
249  $res[$key][] = $a;
250  }
251  ApiResult::setIndexedTagName( $res[$key], 'msg' );
252  break;
253  }
254  }
255 
260  private function getModuleInfo( $module ) {
261  $ret = [];
262  $path = $module->getModulePath();
263  $paramValidator = $module->getMain()->getParamValidator();
264 
265  $ret['name'] = $module->getModuleName();
266  $ret['classname'] = get_class( $module );
267  $ret['path'] = $path;
268  if ( !$module->isMain() ) {
269  $ret['group'] = $module->getParent()->getModuleManager()->getModuleGroup(
270  $module->getModuleName()
271  );
272  }
273  $ret['prefix'] = $module->getModulePrefix();
274 
275  $sourceInfo = $module->getModuleSourceInfo();
276  if ( $sourceInfo ) {
277  $ret['source'] = $sourceInfo['name'];
278  if ( isset( $sourceInfo['namemsg'] ) ) {
279  $ret['sourcename'] = $this->context->msg( $sourceInfo['namemsg'] )->text();
280  } else {
281  $ret['sourcename'] = $ret['source'];
282  }
283 
284  $link = SpecialPage::getTitleFor( 'Version', 'License/' . $sourceInfo['name'] )->getFullURL();
285  if ( isset( $sourceInfo['license-name'] ) ) {
286  $ret['licensetag'] = $sourceInfo['license-name'];
287  $ret['licenselink'] = (string)$link;
288  } elseif ( ExtensionInfo::getLicenseFileNames( dirname( $sourceInfo['path'] ) ) ) {
289  $ret['licenselink'] = (string)$link;
290  }
291  }
292 
293  $this->formatHelpMessages( $ret, 'description', $module->getFinalDescription() );
294 
295  foreach ( $module->getHelpFlags() as $flag ) {
296  $ret[$flag] = true;
297  }
298 
299  $ret['helpurls'] = (array)$module->getHelpUrls();
300  if ( isset( $ret['helpurls'][0] ) && $ret['helpurls'][0] === false ) {
301  $ret['helpurls'] = [];
302  }
303  // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive
304  ApiResult::setIndexedTagName( $ret['helpurls'], 'helpurl' );
305 
306  if ( $this->helpFormat !== 'none' ) {
307  $ret['examples'] = [];
308  $examples = $module->getExamplesMessages();
309  foreach ( $examples as $qs => $msg ) {
310  $item = [
311  'query' => $qs
312  ];
313  $msg = ApiBase::makeMessage( $msg, $this->context, [
314  $module->getModulePrefix(),
315  $module->getModuleName(),
316  $module->getModulePath()
317  ] );
318  $this->formatHelpMessages( $item, 'description', [ $msg ] );
319  if ( isset( $item['description'] ) ) {
320  if ( is_array( $item['description'] ) ) {
321  $item['description'] = $item['description'][0];
322  } else {
323  ApiResult::setSubelementsList( $item, 'description' );
324  }
325  }
326  $ret['examples'][] = $item;
327  }
328  ApiResult::setIndexedTagName( $ret['examples'], 'example' );
329  }
330 
331  $ret['parameters'] = [];
332  $ret['templatedparameters'] = [];
333  $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
334  $paramDesc = $module->getFinalParamDescription();
335  $index = 0;
336  foreach ( $params as $name => $settings ) {
337  $settings = $paramValidator->normalizeSettings( $settings );
338 
339  $item = [
340  'index' => ++$index,
341  'name' => $name,
342  ];
343 
344  if ( !empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
345  $item['templatevars'] = $settings[ApiBase::PARAM_TEMPLATE_VARS];
346  ApiResult::setIndexedTagName( $item['templatevars'], 'var' );
347  }
348 
349  if ( isset( $paramDesc[$name] ) ) {
350  $this->formatHelpMessages( $item, 'description', $paramDesc[$name], true );
351  }
352 
353  foreach ( $paramValidator->getParamInfo( $module, $name, $settings, [] ) as $k => $v ) {
354  $item[$k] = $v;
355  }
356 
357  if ( $name === 'token' && $module->needsToken() ) {
358  $item['tokentype'] = $module->needsToken();
359  }
360 
361  if ( $item['type'] === 'NULL' ) {
362  // Munge "NULL" to "string" for historical reasons
363  $item['type'] = 'string';
364  } elseif ( is_array( $item['type'] ) ) {
365  // Set indexed tag name, for historical reasons
366  ApiResult::setIndexedTagName( $item['type'], 't' );
367  }
368 
369  if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
370  $item['info'] = [];
371  foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
372  $tag = array_shift( $i );
373  $info = [
374  'name' => $tag,
375  ];
376  if ( count( $i ) ) {
377  $info['values'] = $i;
378  ApiResult::setIndexedTagName( $info['values'], 'v' );
379  }
380  $this->formatHelpMessages( $info, 'text', [
381  $this->context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
382  ->numParams( count( $i ) )
383  ->params( $this->context->getLanguage()->commaList( $i ) )
384  ->params( $module->getModulePrefix() )
385  ] );
386  ApiResult::setSubelementsList( $info, 'text' );
387  $item['info'][] = $info;
388  }
389  ApiResult::setIndexedTagName( $item['info'], 'i' );
390  }
391 
392  $key = empty( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ? 'parameters' : 'templatedparameters';
393  $ret[$key][] = $item;
394  }
395  ApiResult::setIndexedTagName( $ret['parameters'], 'param' );
396  ApiResult::setIndexedTagName( $ret['templatedparameters'], 'param' );
397 
398  $dynamicParams = $module->dynamicParameterDocumentation();
399  if ( $dynamicParams !== null ) {
400  if ( $this->helpFormat === 'none' ) {
401  $ret['dynamicparameters'] = true;
402  } else {
403  $dynamicParams = ApiBase::makeMessage( $dynamicParams, $this->context, [
404  $module->getModulePrefix(),
405  $module->getModuleName(),
406  $module->getModulePath()
407  ] );
408  $this->formatHelpMessages( $ret, 'dynamicparameters', [ $dynamicParams ] );
409  }
410  }
411 
412  return $ret;
413  }
414 
415  public function isReadMode() {
416  return false;
417  }
418 
419  public function getAllowedParams() {
420  // back compat
421  $querymodules = $this->getMain()->getModuleManager()
422  ->getModule( 'query' )->getModuleManager()->getNames();
423  sort( $querymodules );
424  $formatmodules = $this->getMain()->getModuleManager()->getNames( 'format' );
425  sort( $formatmodules );
426 
427  return [
428  'modules' => [
429  ParamValidator::PARAM_ISMULTI => true,
430  ],
431  'helpformat' => [
432  ParamValidator::PARAM_DEFAULT => 'none',
433  ParamValidator::PARAM_TYPE => [ 'html', 'wikitext', 'raw', 'none' ],
434  ],
435 
436  'querymodules' => [
437  ParamValidator::PARAM_DEPRECATED => true,
438  ParamValidator::PARAM_ISMULTI => true,
439  ParamValidator::PARAM_TYPE => $querymodules,
440  ],
441  'mainmodule' => [
442  ParamValidator::PARAM_DEPRECATED => true,
443  ],
444  'pagesetmodule' => [
445  ParamValidator::PARAM_DEPRECATED => true,
446  ],
447  'formatmodules' => [
448  ParamValidator::PARAM_DEPRECATED => true,
449  ParamValidator::PARAM_ISMULTI => true,
450  ParamValidator::PARAM_TYPE => $formatmodules,
451  ]
452  ];
453  }
454 
455  protected function getExamplesMessages() {
456  return [
457  'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo'
458  => 'apihelp-paraminfo-example-1',
459  'action=paraminfo&modules=query%2B*'
460  => 'apihelp-paraminfo-example-2',
461  ];
462  }
463 
464  public function getHelpUrls() {
465  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parameter_information';
466  }
467 }
$modules
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:62
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:537
getModuleFromPath( $path)
Get a module from its module path.
Definition: ApiBase.php:627
static makeMessage( $msg, IContextSource $context, array $params=null)
Create a Message from a string or array.
Definition: ApiBase.php:1267
getParent()
Get the parent of this module.
Definition: ApiBase.php:567
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiBase.php:370
getModuleManager()
Get the module manager, or null if this module has no submodules.
Definition: ApiBase.php:327
getMain()
Get the main module.
Definition: ApiBase.php:546
const PARAM_HELP_MSG_INFO
(array) Specify additional information tags for the parameter.
Definition: ApiBase.php:186
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiBase.php:359
getModuleSourceInfo()
Returns information about the source of this module, if known.
Definition: ApiBase.php:2100
isMain()
Returns true if this module is the main module ($this === $this->mMainModule), false otherwise.
Definition: ApiBase.php:556
getFinalDescription()
Get the final module description, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:1859
const PARAM_TEMPLATE_VARS
(array) Indicate that this is a templated parameter, and specify replacements.
Definition: ApiBase.php:227
getResult()
Get the result object.
Definition: ApiBase.php:667
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:807
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiBase.php:482
getModulePath()
Get the path to this module.
Definition: ApiBase.php:607
getHelpFlags()
Generates the list of flags for the help screen and for action=paraminfo.
Definition: ApiBase.php:2067
getFinalParams( $flags=0)
Get the final list of parameters, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:1888
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1433
const GET_VALUES_FOR_HELP
getAllowedParams() flag: When this is set, the result could take longer to generate,...
Definition: ApiBase.php:247
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:528
getFinalParamDescription()
Get final parameter descriptions, after hooks have had a chance to tweak it as needed.
Definition: ApiBase.php:1919
dynamicParameterDocumentation()
Indicate if the module supports dynamically-determined parameters that cannot be included in self::ge...
Definition: ApiBase.php:773
Message subclass that prepends wikitext for API help.
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:64
This class contains a list of pages that the client has requested.
Definition: ApiPageSet.php:55
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
isReadMode()
Indicates whether this module requires read rights.
getHelpUrls()
Return links to more detailed help pages about the module.
__construct(ApiMain $main, $action, UserFactory $userFactory)
formatHelpMessages(array &$res, $key, array $msgs, $joinLists=false)
getExamplesMessages()
Returns usage examples for this module.
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
Definition: ApiResult.php:553
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
Exception used to abort API execution with an error.
getStatusValue()
Fetch the error status.
Parent class for all special pages.
Definition: SpecialPage.php:65
Creates User objects.
Definition: UserFactory.php:41
static stripOuterParagraph( $html)
Strip outer.
Definition: Parser.php:6352
Group all the pieces relevant to the context of a request into one instance.
setUser(User $user)
Service for formatting and validating API parameters.