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