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