MediaWiki  master
ApiQuery.php
Go to the documentation of this file.
1 <?php
25 use Wikimedia\ObjectFactory\ObjectFactory;
29 
41 class ApiQuery extends ApiBase {
42 
46  private const QUERY_PROP_MODULES = [
47  'categories' => [
48  'class' => ApiQueryCategories::class,
49  ],
50  'categoryinfo' => [
51  'class' => ApiQueryCategoryInfo::class,
52  ],
53  'contributors' => [
54  'class' => ApiQueryContributors::class,
55  'services' => [
56  'RevisionStore',
57  'ActorMigration',
58  'UserGroupManager',
59  'GroupPermissionsLookup',
60  ]
61  ],
62  'deletedrevisions' => [
63  'class' => ApiQueryDeletedRevisions::class,
64  'services' => [
65  'RevisionStore',
66  'ContentHandlerFactory',
67  'ParserFactory',
68  'SlotRoleRegistry',
69  'ChangeTagDefStore',
70  'LinkBatchFactory',
71  'ContentRenderer',
72  'ContentTransformer',
73  'CommentFormatter',
74  ]
75  ],
76  'duplicatefiles' => [
77  'class' => ApiQueryDuplicateFiles::class,
78  'services' => [
79  'RepoGroup',
80  ]
81  ],
82  'extlinks' => [
83  'class' => ApiQueryExternalLinks::class,
84  ],
85  'fileusage' => [
86  'class' => ApiQueryBacklinksprop::class,
87  'services' => [
88  // Same as for linkshere, redirects, transcludedin
89  'LinksMigration',
90  ]
91  ],
92  'images' => [
93  'class' => ApiQueryImages::class,
94  ],
95  'imageinfo' => [
96  'class' => ApiQueryImageInfo::class,
97  'services' => [
98  // Same as for stashimageinfo
99  'RepoGroup',
100  'ContentLanguage',
101  'BadFileLookup',
102  ]
103  ],
104  'info' => [
105  'class' => ApiQueryInfo::class,
106  'services' => [
107  'ContentLanguage',
108  'LinkBatchFactory',
109  'NamespaceInfo',
110  'TitleFactory',
111  'TitleFormatter',
112  'WatchedItemStore',
113  'LanguageConverterFactory',
114  'RestrictionStore',
115  'LinksMigration',
116  ],
117  ],
118  'links' => [
119  'class' => ApiQueryLinks::class,
120  'services' => [
121  // Same as for templates
122  'LinkBatchFactory',
123  'LinksMigration',
124  ]
125  ],
126  'linkshere' => [
127  'class' => ApiQueryBacklinksprop::class,
128  'services' => [
129  // Same as for fileusage, redirects, transcludedin
130  'LinksMigration',
131  ]
132  ],
133  'iwlinks' => [
134  'class' => ApiQueryIWLinks::class,
135  ],
136  'langlinks' => [
137  'class' => ApiQueryLangLinks::class,
138  'services' => [
139  'LanguageNameUtils',
140  'ContentLanguage',
141  ]
142  ],
143  'pageprops' => [
144  'class' => ApiQueryPageProps::class,
145  'services' => [
146  'PageProps',
147  ]
148  ],
149  'redirects' => [
150  'class' => ApiQueryBacklinksprop::class,
151  'services' => [
152  // Same as for fileusage, linkshere, transcludedin
153  'LinksMigration',
154  ]
155  ],
156  'revisions' => [
157  'class' => ApiQueryRevisions::class,
158  'services' => [
159  'RevisionStore',
160  'ContentHandlerFactory',
161  'ParserFactory',
162  'SlotRoleRegistry',
163  'ChangeTagDefStore',
164  'ActorMigration',
165  'ContentRenderer',
166  'ContentTransformer',
167  'CommentFormatter',
168  ]
169  ],
170  'stashimageinfo' => [
171  'class' => ApiQueryStashImageInfo::class,
172  'services' => [
173  // Same as for imageinfo
174  'RepoGroup',
175  'ContentLanguage',
176  'BadFileLookup',
177  ]
178  ],
179  'templates' => [
180  'class' => ApiQueryLinks::class,
181  'services' => [
182  // Same as for links
183  'LinkBatchFactory',
184  'LinksMigration',
185  ]
186  ],
187  'transcludedin' => [
188  'class' => ApiQueryBacklinksprop::class,
189  'services' => [
190  // Same as for fileusage, linkshere, redirects
191  'LinksMigration',
192  ]
193  ],
194  ];
195 
199  private const QUERY_LIST_MODULES = [
200  'allcategories' => [
201  'class' => ApiQueryAllCategories::class,
202  ],
203  'alldeletedrevisions' => [
204  'class' => ApiQueryAllDeletedRevisions::class,
205  'services' => [
206  'RevisionStore',
207  'ContentHandlerFactory',
208  'ParserFactory',
209  'SlotRoleRegistry',
210  'ChangeTagDefStore',
211  'NamespaceInfo',
212  'ContentRenderer',
213  'ContentTransformer',
214  'CommentFormatter',
215  ]
216  ],
217  'allfileusages' => [
218  'class' => ApiQueryAllLinks::class,
219  'services' => [
220  // Same as for alllinks, allredirects, alltransclusions
221  'NamespaceInfo',
222  'GenderCache',
223  'LinksMigration',
224  ]
225  ],
226  'allimages' => [
227  'class' => ApiQueryAllImages::class,
228  'services' => [
229  'RepoGroup',
230  'GroupPermissionsLookup',
231  ]
232  ],
233  'alllinks' => [
234  'class' => ApiQueryAllLinks::class,
235  'services' => [
236  // Same as for allfileusages, allredirects, alltransclusions
237  'NamespaceInfo',
238  'GenderCache',
239  'LinksMigration',
240  ]
241  ],
242  'allpages' => [
243  'class' => ApiQueryAllPages::class,
244  'services' => [
245  'NamespaceInfo',
246  'GenderCache',
247  'RestrictionStore',
248  ]
249  ],
250  'allredirects' => [
251  'class' => ApiQueryAllLinks::class,
252  'services' => [
253  // Same as for allfileusages, alllinks, alltransclusions
254  'NamespaceInfo',
255  'GenderCache',
256  'LinksMigration',
257  ]
258  ],
259  'allrevisions' => [
260  'class' => ApiQueryAllRevisions::class,
261  'services' => [
262  'RevisionStore',
263  'ContentHandlerFactory',
264  'ParserFactory',
265  'SlotRoleRegistry',
266  'ActorMigration',
267  'NamespaceInfo',
268  'ContentRenderer',
269  'ContentTransformer',
270  'CommentFormatter',
271  ]
272  ],
273  'mystashedfiles' => [
274  'class' => ApiQueryMyStashedFiles::class,
275  ],
276  'alltransclusions' => [
277  'class' => ApiQueryAllLinks::class,
278  'services' => [
279  // Same as for allfileusages, alllinks, allredirects
280  'NamespaceInfo',
281  'GenderCache',
282  'LinksMigration',
283  ]
284  ],
285  'allusers' => [
286  'class' => ApiQueryAllUsers::class,
287  'services' => [
288  'UserFactory',
289  'UserGroupManager',
290  'GroupPermissionsLookup',
291  'ContentLanguage',
292  ]
293  ],
294  'backlinks' => [
295  'class' => ApiQueryBacklinks::class,
296  'services' => [
297  'LinksMigration',
298  ]
299  ],
300  'blocks' => [
301  'class' => ApiQueryBlocks::class,
302  'services' => [
303  'BlockActionInfo',
304  'BlockRestrictionStore',
305  'CommentStore',
306  ],
307  ],
308  'categorymembers' => [
309  'class' => ApiQueryCategoryMembers::class,
310  'services' => [
311  'CollationFactory',
312  ]
313  ],
314  'deletedrevs' => [
315  'class' => ApiQueryDeletedrevs::class,
316  'services' => [
317  'CommentStore',
318  'RowCommentFormatter',
319  'RevisionStore',
320  'ChangeTagDefStore',
321  'LinkBatchFactory',
322  ],
323  ],
324  'embeddedin' => [
325  'class' => ApiQueryBacklinks::class,
326  'services' => [
327  'LinksMigration',
328  ]
329  ],
330  'exturlusage' => [
331  'class' => ApiQueryExtLinksUsage::class,
332  ],
333  'filearchive' => [
334  'class' => ApiQueryFilearchive::class,
335  'services' => [
336  'CommentStore',
337  'CommentFormatter',
338  ],
339  ],
340  'imageusage' => [
341  'class' => ApiQueryBacklinks::class,
342  'services' => [
343  'LinksMigration',
344  ]
345  ],
346  'iwbacklinks' => [
347  'class' => ApiQueryIWBacklinks::class,
348  ],
349  'langbacklinks' => [
350  'class' => ApiQueryLangBacklinks::class,
351  ],
352  'logevents' => [
353  'class' => ApiQueryLogEvents::class,
354  'services' => [
355  'CommentStore',
356  'RowCommentFormatter',
357  'ChangeTagDefStore',
358  ],
359  ],
360  'pageswithprop' => [
361  'class' => ApiQueryPagesWithProp::class,
362  ],
363  'pagepropnames' => [
364  'class' => ApiQueryPagePropNames::class,
365  ],
366  'prefixsearch' => [
367  'class' => ApiQueryPrefixSearch::class,
368  'services' => [
369  'SearchEngineConfig',
370  'SearchEngineFactory',
371  ],
372  ],
373  'protectedtitles' => [
374  'class' => ApiQueryProtectedTitles::class,
375  'services' => [
376  'CommentStore',
377  'RowCommentFormatter'
378  ],
379  ],
380  'querypage' => [
381  'class' => ApiQueryQueryPage::class,
382  'services' => [
383  'SpecialPageFactory',
384  ]
385  ],
386  'random' => [
387  'class' => ApiQueryRandom::class,
388  ],
389  'recentchanges' => [
390  'class' => ApiQueryRecentChanges::class,
391  'services' => [
392  'CommentStore',
393  'RowCommentFormatter',
394  'ChangeTagDefStore',
395  'SlotRoleStore',
396  'SlotRoleRegistry',
397  ],
398  ],
399  'search' => [
400  'class' => ApiQuerySearch::class,
401  'services' => [
402  'SearchEngineConfig',
403  'SearchEngineFactory',
404  ],
405  ],
406  'tags' => [
407  'class' => ApiQueryTags::class,
408  ],
409  'usercontribs' => [
410  'class' => ApiQueryUserContribs::class,
411  'services' => [
412  'CommentStore',
413  'UserIdentityLookup',
414  'UserNameUtils',
415  'RevisionStore',
416  'ChangeTagDefStore',
417  'ActorMigration',
418  'CommentFormatter',
419  ],
420  ],
421  'users' => [
422  'class' => ApiQueryUsers::class,
423  'services' => [
424  'UserNameUtils',
425  'UserFactory',
426  'UserGroupManager',
427  'GenderCache',
428  'AuthManager',
429  ],
430  ],
431  'watchlist' => [
432  'class' => ApiQueryWatchlist::class,
433  'services' => [
434  'CommentStore',
435  'WatchedItemQueryService',
436  'ContentLanguage',
437  'NamespaceInfo',
438  'GenderCache',
439  'CommentFormatter',
440  ],
441  ],
442  'watchlistraw' => [
443  'class' => ApiQueryWatchlistRaw::class,
444  'services' => [
445  'WatchedItemQueryService',
446  'ContentLanguage',
447  'NamespaceInfo',
448  'GenderCache',
449  ]
450  ],
451  ];
452 
456  private const QUERY_META_MODULES = [
457  'allmessages' => [
458  'class' => ApiQueryAllMessages::class,
459  'services' => [
460  'ContentLanguage',
461  'LanguageFactory',
462  'LanguageNameUtils',
463  'LocalisationCache',
464  'MessageCache',
465  ]
466  ],
467  'authmanagerinfo' => [
468  'class' => ApiQueryAuthManagerInfo::class,
469  'services' => [
470  'AuthManager',
471  ]
472  ],
473  'siteinfo' => [
474  'class' => ApiQuerySiteinfo::class,
475  'services' => [
476  'UserOptionsLookup',
477  'UserGroupManager',
478  'LanguageConverterFactory',
479  'LanguageFactory',
480  'LanguageNameUtils',
481  'ContentLanguage',
482  'NamespaceInfo',
483  'InterwikiLookup',
484  'Parser',
485  'MagicWordFactory',
486  'SpecialPageFactory',
487  'SkinFactory',
488  'DBLoadBalancer',
489  'ReadOnlyMode',
490  ]
491  ],
492  'userinfo' => [
493  'class' => ApiQueryUserInfo::class,
494  'services' => [
495  'TalkPageNotificationManager',
496  'WatchedItemStore',
497  'UserEditTracker',
498  'UserOptionsLookup',
499  'UserGroupManager',
500  ]
501  ],
502  'filerepoinfo' => [
503  'class' => ApiQueryFileRepoInfo::class,
504  'services' => [
505  'RepoGroup',
506  ]
507  ],
508  'tokens' => [
509  'class' => ApiQueryTokens::class,
510  ],
511  'languageinfo' => [
512  'class' => ApiQueryLanguageinfo::class,
513  'services' => [
514  'LanguageFactory',
515  'LanguageNameUtils',
516  'LanguageFallback',
517  'LanguageConverterFactory',
518  ],
519  ],
520  ];
521 
525  private $mPageSet;
526 
527  private $mParams;
528  private $mNamedDB = [];
529  private $mModuleMgr;
530 
532  private $loadBalancer;
533 
535  private $wikiExporterFactory;
536 
544  public function __construct(
545  ApiMain $main,
546  $action,
547  ObjectFactory $objectFactory,
548  ILoadBalancer $loadBalancer,
549  WikiExporterFactory $wikiExporterFactory
550  ) {
551  parent::__construct( $main, $action );
552 
553  $this->mModuleMgr = new ApiModuleManager(
554  $this,
555  $objectFactory
556  );
557 
558  // Allow custom modules to be added in LocalSettings.php
559  $config = $this->getConfig();
560  $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES, 'prop' );
561  $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIPropModules ), 'prop' );
562  $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES, 'list' );
563  $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIListModules ), 'list' );
564  $this->mModuleMgr->addModules( self::QUERY_META_MODULES, 'meta' );
565  $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIMetaModules ), 'meta' );
566 
567  $this->getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
568 
569  // Create PageSet that will process titles/pageids/revids/generator
570  $this->mPageSet = new ApiPageSet( $this );
571  $this->loadBalancer = $loadBalancer;
572  $this->wikiExporterFactory = $wikiExporterFactory;
573  }
574 
579  public function getModuleManager() {
580  return $this->mModuleMgr;
581  }
582 
597  public function getNamedDB( $name, $db, $groups ) {
598  wfDeprecated( __METHOD__, '1.39' );
599  if ( !array_key_exists( $name, $this->mNamedDB ) ) {
600  $this->mNamedDB[$name] = $this->loadBalancer->getConnectionRef( $db, $groups );
601  }
602 
603  return $this->mNamedDB[$name];
604  }
605 
610  public function getPageSet() {
611  return $this->mPageSet;
612  }
613 
617  public function getCustomPrinter() {
618  // If &exportnowrap is set, use the raw formatter
619  if ( $this->getParameter( 'export' ) &&
620  $this->getParameter( 'exportnowrap' )
621  ) {
622  return new ApiFormatRaw( $this->getMain(),
623  $this->getMain()->createPrinterByName( 'xml' ) );
624  } else {
625  return null;
626  }
627  }
628 
639  public function execute() {
640  $this->mParams = $this->extractRequestParams();
641 
642  // Instantiate requested modules
643  $allModules = [];
644  $this->instantiateModules( $allModules, 'prop' );
645  $propModules = array_keys( $allModules );
646  $this->instantiateModules( $allModules, 'list' );
647  $this->instantiateModules( $allModules, 'meta' );
648 
649  // Filter modules based on continue parameter
650  $continuationManager = new ApiContinuationManager( $this, $allModules, $propModules );
651  $this->setContinuationManager( $continuationManager );
653  $modules = $continuationManager->getRunModules();
654  '@phan-var ApiQueryBase[] $modules';
655 
656  if ( !$continuationManager->isGeneratorDone() ) {
657  // Query modules may optimize data requests through the $this->getPageSet()
658  // object by adding extra fields from the page table.
659  foreach ( $modules as $module ) {
660  $module->requestExtraData( $this->mPageSet );
661  }
662  // Populate page/revision information
663  $this->mPageSet->execute();
664  // Record page information (title, namespace, if exists, etc)
665  $this->outputGeneralPageInfo();
666  } else {
667  $this->mPageSet->executeDryRun();
668  }
669 
670  $cacheMode = $this->mPageSet->getCacheMode();
671 
672  // Execute all unfinished modules
673  foreach ( $modules as $module ) {
674  $params = $module->extractRequestParams();
675  $cacheMode = $this->mergeCacheMode(
676  $cacheMode, $module->getCacheMode( $params ) );
677  $module->execute();
678  $this->getHookRunner()->onAPIQueryAfterExecute( $module );
679  }
680 
681  // Set the cache mode
682  $this->getMain()->setCacheMode( $cacheMode );
683 
684  // Write the continuation data into the result
685  $this->setContinuationManager( null );
686  if ( $this->mParams['rawcontinue'] ) {
687  $data = $continuationManager->getRawNonContinuation();
688  if ( $data ) {
689  $this->getResult()->addValue( null, 'query-noncontinue', $data,
691  }
692  $data = $continuationManager->getRawContinuation();
693  if ( $data ) {
694  $this->getResult()->addValue( null, 'query-continue', $data,
696  }
697  } else {
698  $continuationManager->setContinuationIntoResult( $this->getResult() );
699  }
700  }
701 
711  protected function mergeCacheMode( $cacheMode, $modCacheMode ) {
712  if ( $modCacheMode === 'anon-public-user-private' ) {
713  if ( $cacheMode !== 'private' ) {
714  $cacheMode = 'anon-public-user-private';
715  }
716  } elseif ( $modCacheMode === 'public' ) {
717  // do nothing, if it's public already it will stay public
718  } else {
719  $cacheMode = 'private';
720  }
721 
722  return $cacheMode;
723  }
724 
730  private function instantiateModules( &$modules, $param ) {
731  $wasPosted = $this->getRequest()->wasPosted();
732  if ( isset( $this->mParams[$param] ) ) {
733  foreach ( $this->mParams[$param] as $moduleName ) {
734  $instance = $this->mModuleMgr->getModule( $moduleName, $param );
735  if ( $instance === null ) {
736  ApiBase::dieDebug( __METHOD__, 'Error instantiating module' );
737  }
738  if ( !$wasPosted && $instance->mustBePosted() ) {
739  $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $moduleName ] );
740  }
741  // Ignore duplicates. TODO 2.0: die()?
742  if ( !array_key_exists( $moduleName, $modules ) ) {
743  $modules[$moduleName] = $instance;
744  }
745  }
746  }
747  }
748 
754  private function outputGeneralPageInfo() {
755  $pageSet = $this->getPageSet();
756  $result = $this->getResult();
757 
758  // We can't really handle max-result-size failure here, but we need to
759  // check anyway in case someone set the limit stupidly low.
760  $fit = true;
761 
762  $values = $pageSet->getNormalizedTitlesAsResult( $result );
763  if ( $values ) {
764  // @phan-suppress-next-line PhanRedundantCondition
765  $fit = $fit && $result->addValue( 'query', 'normalized', $values );
766  }
767  $values = $pageSet->getConvertedTitlesAsResult( $result );
768  if ( $values ) {
769  $fit = $fit && $result->addValue( 'query', 'converted', $values );
770  }
771  $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams['iwurl'] );
772  if ( $values ) {
773  $fit = $fit && $result->addValue( 'query', 'interwiki', $values );
774  }
775  $values = $pageSet->getRedirectTitlesAsResult( $result );
776  if ( $values ) {
777  $fit = $fit && $result->addValue( 'query', 'redirects', $values );
778  }
779  $values = $pageSet->getMissingRevisionIDsAsResult( $result );
780  if ( $values ) {
781  $fit = $fit && $result->addValue( 'query', 'badrevids', $values );
782  }
783 
784  // Page elements
785  $pages = [];
786 
787  // Report any missing titles
788  foreach ( $pageSet->getMissingTitles() as $fakeId => $title ) {
789  $vals = [];
791  $vals['missing'] = true;
792  if ( $title->isKnown() ) {
793  $vals['known'] = true;
794  }
795  $pages[$fakeId] = $vals;
796  }
797  // Report any invalid titles
798  foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
799  $pages[$fakeId] = $data + [ 'invalid' => true ];
800  }
801  // Report any missing page ids
802  foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
803  $pages[$pageid] = [
804  'pageid' => $pageid,
805  'missing' => true,
806  ];
807  }
808  // Report special pages
810  foreach ( $pageSet->getSpecialTitles() as $fakeId => $title ) {
811  $vals = [];
813  $vals['special'] = true;
814  if ( !$title->isKnown() ) {
815  $vals['missing'] = true;
816  }
817  $pages[$fakeId] = $vals;
818  }
819 
820  // Output general page information for found titles
821  foreach ( $pageSet->getGoodTitles() as $pageid => $title ) {
822  $vals = [];
823  $vals['pageid'] = $pageid;
825  $pages[$pageid] = $vals;
826  }
827 
828  if ( count( $pages ) ) {
829  $pageSet->populateGeneratorData( $pages );
830  ApiResult::setArrayType( $pages, 'BCarray' );
831 
832  if ( $this->mParams['indexpageids'] ) {
833  $pageIDs = array_keys( ApiResult::stripMetadataNonRecursive( $pages ) );
834  // json treats all map keys as strings - converting to match
835  $pageIDs = array_map( 'strval', $pageIDs );
836  ApiResult::setIndexedTagName( $pageIDs, 'id' );
837  $fit = $fit && $result->addValue( 'query', 'pageids', $pageIDs );
838  }
839 
840  ApiResult::setIndexedTagName( $pages, 'page' );
841  $fit = $fit && $result->addValue( 'query', 'pages', $pages );
842  }
843 
844  if ( !$fit ) {
845  $this->dieWithError( 'apierror-badconfig-resulttoosmall', 'badconfig' );
846  }
847 
848  if ( $this->mParams['export'] ) {
849  $this->doExport( $pageSet, $result );
850  }
851  }
852 
857  private function doExport( $pageSet, $result ) {
858  $exportTitles = [];
859  $titles = $pageSet->getGoodPages();
860  if ( count( $titles ) ) {
862  foreach ( $titles as $title ) {
863  if ( $this->getAuthority()->authorizeRead( 'read', $title ) ) {
864  $exportTitles[] = $title;
865  }
866  }
867  }
868 
869  $exporter = $this->wikiExporterFactory->getWikiExporter( $this->getDB() );
870  $sink = new DumpStringOutput;
871  $exporter->setOutputSink( $sink );
872  $exporter->setSchemaVersion( $this->mParams['exportschema'] );
873  $exporter->openStream();
874  foreach ( $exportTitles as $title ) {
875  $exporter->pageByTitle( $title );
876  }
877  $exporter->closeStream();
878 
879  // Don't check the size of exported stuff
880  // It's not continuable, so it would cause more
881  // problems than it'd solve
882  if ( $this->mParams['exportnowrap'] ) {
883  $result->reset();
884  // Raw formatter will handle this
885  $result->addValue( null, 'text', $sink, ApiResult::NO_SIZE_CHECK );
886  $result->addValue( null, 'mime', 'text/xml', ApiResult::NO_SIZE_CHECK );
887  $result->addValue( null, 'filename', 'export.xml', ApiResult::NO_SIZE_CHECK );
888  } else {
889  $result->addValue( 'query', 'export', $sink, ApiResult::NO_SIZE_CHECK );
890  $result->addValue( 'query', ApiResult::META_BC_SUBELEMENTS, [ 'export' ] );
891  }
892  }
893 
894  public function getAllowedParams( $flags = 0 ) {
895  $result = [
896  'prop' => [
897  ParamValidator::PARAM_ISMULTI => true,
898  ParamValidator::PARAM_TYPE => 'submodule',
899  ],
900  'list' => [
901  ParamValidator::PARAM_ISMULTI => true,
902  ParamValidator::PARAM_TYPE => 'submodule',
903  ],
904  'meta' => [
905  ParamValidator::PARAM_ISMULTI => true,
906  ParamValidator::PARAM_TYPE => 'submodule',
907  ],
908  'indexpageids' => false,
909  'export' => false,
910  'exportnowrap' => false,
911  'exportschema' => [
912  ParamValidator::PARAM_DEFAULT => WikiExporter::schemaVersion(),
913  ParamValidator::PARAM_TYPE => XmlDumpWriter::$supportedSchemas,
914  ],
915  'iwurl' => false,
916  'continue' => [
917  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
918  ],
919  'rawcontinue' => false,
920  ];
921  if ( $flags ) {
922  $result += $this->getPageSet()->getFinalParams( $flags );
923  }
924 
925  return $result;
926  }
927 
928  public function isReadMode() {
929  // We need to make an exception for certain meta modules that should be
930  // accessible even without the 'read' right. Restrict the exception as
931  // much as possible: no other modules allowed, and no pageset
932  // parameters either. We do allow the 'rawcontinue' and 'indexpageids'
933  // parameters since frameworks might add these unconditionally and they
934  // can't expose anything here.
935  $allowedParams = [ 'rawcontinue' => 1, 'indexpageids' => 1 ];
936  $this->mParams = $this->extractRequestParams();
937  $request = $this->getRequest();
938  foreach ( $this->mParams + $this->getPageSet()->extractRequestParams() as $param => $value ) {
939  $needed = $param === 'meta';
940  if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
941  return true;
942  }
943  }
944 
945  // Ask each module if it requires read mode. Any true => this returns
946  // true.
947  $modules = [];
948  $this->instantiateModules( $modules, 'meta' );
949  foreach ( $modules as $module ) {
950  if ( $module->isReadMode() ) {
951  return true;
952  }
953  }
954 
955  return false;
956  }
957 
958  protected function getExamplesMessages() {
959  return [
960  'action=query&prop=revisions&meta=siteinfo&' .
961  'titles=Main%20Page&rvprop=user|comment&continue='
962  => 'apihelp-query-example-revisions',
963  'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
964  => 'apihelp-query-example-allpages',
965  ];
966  }
967 
968  public function getHelpUrls() {
969  return [
970  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
971  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
972  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
973  'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',
974  ];
975  }
976 }
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
$modules
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:58
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition: ApiBase.php:1459
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:893
getDB()
Gets a default replica DB connection object.
Definition: ApiBase.php:659
dieWithErrorOrDebug( $msg, $code=null, $data=null, $httpCode=null)
Will only set a warning instead of failing if the global $wgDebugAPI is set to true.
Definition: ApiBase.php:1630
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:1700
getMain()
Get the main module.
Definition: ApiBase.php:521
setContinuationManager(ApiContinuationManager $manager=null)
Definition: ApiBase.php:680
getResult()
Get the result object.
Definition: ApiBase.php:636
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:772
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:165
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition: ApiBase.php:718
This manages continuation state.
Formatter that spits out anything you like with any desired MIME type.
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:56
This class holds a list of modules and handles instantiation.
This class contains a list of pages that the client has requested.
Definition: ApiPageSet.php:52
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
This is the main query class.
Definition: ApiQuery.php:41
isReadMode()
Indicates whether this module requires read rights.
Definition: ApiQuery.php:928
mergeCacheMode( $cacheMode, $modCacheMode)
Update a cache mode string, applying the cache mode of a new module to it.
Definition: ApiQuery.php:711
getAllowedParams( $flags=0)
Definition: ApiQuery.php:894
getModuleManager()
Overrides to return this instance's module manager.
Definition: ApiQuery.php:579
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiQuery.php:958
__construct(ApiMain $main, $action, ObjectFactory $objectFactory, ILoadBalancer $loadBalancer, WikiExporterFactory $wikiExporterFactory)
Definition: ApiQuery.php:544
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiQuery.php:968
getPageSet()
Gets the set of pages the user has requested (or generated)
Definition: ApiQuery.php:610
execute()
Query execution happens in the following steps: #1 Create a PageSet object with any pages requested b...
Definition: ApiQuery.php:639
getNamedDB( $name, $db, $groups)
Get a cached database connection with a given name.
Definition: ApiQuery.php:597
getCustomPrinter()
Definition: ApiQuery.php:617
static stripMetadataNonRecursive( $data, &$metadata=null)
Remove metadata keys from a data array or object, non-recursive.
Definition: ApiResult.php:1050
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:716
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition: ApiResult.php:58
const ADD_ON_TOP
For addValue(), setValue() and similar functions, if the value does not exist, add it as the first el...
Definition: ApiResult.php:49
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
Definition: ApiResult.php:143
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
Factory service for WikiExporter instances.
A class containing constants representing the names of configuration variables.
static schemaVersion()
Returns the default export schema version, as defined by the XmlDumpSchemaVersion setting.
Service for formatting and validating API parameters.
static string[] $supportedSchemas
the schema versions supported for output @final
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
This class is a delegate to ILBFactory for a given database cluster.
return true
Definition: router.php:90