55 private const QUERY_PROP_MODULES = [
57 'class' => ApiQueryCategories::class,
60 'class' => ApiQueryCategoryInfo::class,
63 'class' => ApiQueryContributors::class,
68 'GroupPermissionsLookup',
72 'deletedrevisions' => [
73 'class' => ApiQueryDeletedRevisions::class,
76 'ContentHandlerFactory',
90 'class' => ApiQueryDuplicateFiles::class,
96 'class' => ApiQueryExternalLinks::class,
102 'class' => ApiQueryBacklinksprop::class,
109 'class' => ApiQueryImages::class,
112 'class' => ApiQueryImageInfo::class,
121 'class' => ApiQueryInfo::class,
129 'LanguageConverterFactory',
134 'IntroMessageBuilder',
135 'PreloadedContentBuilder',
141 'class' => ApiQueryLinks::class,
149 'class' => ApiQueryBacklinksprop::class,
156 'class' => ApiQueryIWLinks::class,
162 'class' => ApiQueryLangLinks::class,
170 'class' => ApiQueryPageProps::class,
176 'class' => ApiQueryBacklinksprop::class,
183 'class' => ApiQueryRevisions::class,
186 'ContentHandlerFactory',
193 'ContentTransformer',
200 'stashimageinfo' => [
201 'class' => ApiQueryStashImageInfo::class,
210 'class' => ApiQueryLinks::class,
218 'class' => ApiQueryBacklinksprop::class,
229 private const QUERY_LIST_MODULES = [
231 'class' => ApiQueryAllCategories::class,
233 'alldeletedrevisions' => [
234 'class' => ApiQueryAllDeletedRevisions::class,
237 'ContentHandlerFactory',
244 'ContentTransformer',
251 'class' => ApiQueryAllLinks::class,
260 'class' => ApiQueryAllImages::class,
263 'GroupPermissionsLookup',
267 'class' => ApiQueryAllLinks::class,
276 'class' => ApiQueryAllPages::class,
284 'class' => ApiQueryAllLinks::class,
293 'class' => ApiQueryAllRevisions::class,
296 'ContentHandlerFactory',
303 'ContentTransformer',
309 'mystashedfiles' => [
310 'class' => ApiQueryMyStashedFiles::class,
312 'alltransclusions' => [
313 'class' => ApiQueryAllLinks::class,
322 'class' => ApiQueryAllUsers::class,
326 'GroupPermissionsLookup',
332 'class' => ApiQueryBacklinks::class,
338 'class' => ApiQueryBlocks::class,
340 'DatabaseBlockStore',
342 'BlockRestrictionStore',
348 'categorymembers' => [
349 'class' => ApiQueryCategoryMembers::class,
355 'class' => ApiQueryCodexIcons::class,
358 'class' => ApiQueryDeletedrevs::class,
361 'RowCommentFormatter',
369 'class' => ApiQueryBacklinks::class,
375 'class' => ApiQueryExtLinksUsage::class,
381 'class' => ApiQueryFilearchive::class,
388 'class' => ApiQueryBacklinks::class,
394 'class' => ApiQueryIWBacklinks::class,
397 'class' => ApiQueryLangBacklinks::class,
400 'class' => ApiQueryLogEvents::class,
403 'RowCommentFormatter',
407 'LogFormatterFactory',
411 'class' => ApiQueryPagesWithProp::class,
414 'class' => ApiQueryPagePropNames::class,
417 'class' => ApiQueryPrefixSearch::class,
419 'SearchEngineConfig',
420 'SearchEngineFactory',
423 'protectedtitles' => [
424 'class' => ApiQueryProtectedTitles::class,
427 'RowCommentFormatter'
431 'class' => ApiQueryQueryPage::class,
433 'SpecialPageFactory',
437 'class' => ApiQueryRandom::class,
439 'ContentHandlerFactory'
443 'class' => ApiQueryRecentChanges::class,
446 'RowCommentFormatter',
453 'LogFormatterFactory',
457 'class' => ApiQuerySearch::class,
459 'SearchEngineConfig',
460 'SearchEngineFactory',
465 'class' => ApiQueryTags::class,
471 'class' => ApiQueryUserContribs::class,
474 'UserIdentityLookup',
484 'class' => ApiQueryUsers::class,
494 'class' => ApiQueryWatchlist::class,
497 'WatchedItemQueryService',
503 'LogFormatterFactory',
507 'class' => ApiQueryWatchlistRaw::class,
509 'WatchedItemQueryService',
520 private const QUERY_META_MODULES = [
522 'class' => ApiQueryAllMessages::class,
531 'authmanagerinfo' => [
532 'class' => ApiQueryAuthManagerInfo::class,
538 'class' => ApiQuerySiteinfo::class,
543 'LanguageConverterFactory',
551 'SpecialPageFactory',
557 'GroupPermissionsLookup',
561 'class' => ApiQueryUserInfo::class,
563 'TalkPageNotificationManager',
571 'class' => ApiQueryFileRepoInfo::class,
577 'class' => ApiQueryTokens::class,
580 'class' => ApiQueryLanguageinfo::class,
585 'LanguageConverterFactory',
607 ObjectFactory $objectFactory,
612 parent::__construct( $main, $action );
621 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES,
'prop' );
623 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES,
'list' );
625 $this->mModuleMgr->addModules( self::QUERY_META_MODULES,
'meta' );
628 $this->
getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
632 $this->wikiExporterFactory = $wikiExporterFactory;
633 $this->titleFormatter = $titleFormatter;
634 $this->titleFactory = $titleFactory;
642 return $this->mModuleMgr;
650 return $this->mPageSet;
662 $this->
getMain()->createPrinterByName(
'xml' ) );
683 $this->instantiateModules( $allModules,
'prop' );
684 $propModules = array_keys( $allModules );
685 $this->instantiateModules( $allModules,
'list' );
686 $this->instantiateModules( $allModules,
'meta' );
692 $modules = $continuationManager->getRunModules();
693 '@phan-var ApiQueryBase[] $modules';
696 $message =
'hookaborted';
697 if ( !$this->
getHookRunner()->onApiQueryCheckCanExecute( $modules, $this->
getUser(), $message ) ) {
703 if ( !$continuationManager->isGeneratorDone() ) {
706 foreach ( $modules as $module ) {
708 $timer = $statsFactory->getTiming(
'api_query_extraDataTiming_seconds' )
709 ->setLabel(
'module', $module->getModuleName() )
710 ->copyToStatsdAt(
'api-query.' . $module->getModuleName() .
'.extraDataTiming' )
712 $module->requestExtraData( $this->mPageSet );
716 $this->mPageSet->execute();
718 $this->outputGeneralPageInfo();
720 $this->mPageSet->executeDryRun();
723 $cacheMode = $this->mPageSet->getCacheMode();
726 foreach ( $modules as $module ) {
728 $timer = $statsFactory->getTiming(
'api_query_executeTiming_seconds' )
729 ->setLabel(
'module', $module->getModuleName() )
730 ->copyToStatsdAt(
'api-query.' . $module->getModuleName() .
'.executeTiming' )
733 $params = $module->extractRequestParams();
735 $cacheMode, $module->getCacheMode( $params ) );
736 $scope = LoggerFactory::getContext()->addScoped( [
737 'context.api_query_module_name' => $module->getModuleName(),
740 ScopedCallback::consume( $scope );
748 $this->
getMain()->setCacheMode( $cacheMode );
752 if ( $this->mParams[
'rawcontinue'] ) {
753 $data = $continuationManager->getRawNonContinuation();
755 $this->
getResult()->addValue(
null,
'query-noncontinue', $data,
758 $data = $continuationManager->getRawContinuation();
760 $this->
getResult()->addValue(
null,
'query-continue', $data,
764 $continuationManager->setContinuationIntoResult( $this->
getResult() );
778 if ( $modCacheMode ===
'anon-public-user-private' ) {
779 if ( $cacheMode !==
'private' ) {
780 $cacheMode =
'anon-public-user-private';
782 } elseif ( $modCacheMode ===
'public' ) {
785 $cacheMode =
'private';
796 private function instantiateModules( &$modules, $param ) {
797 $wasPosted = $this->
getRequest()->wasPosted();
798 if ( isset( $this->mParams[$param] ) ) {
799 foreach ( $this->mParams[$param] as $moduleName ) {
800 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
801 if ( $instance ===
null ) {
804 if ( !$wasPosted && $instance->mustBePosted() ) {
808 if ( !array_key_exists( $moduleName, $modules ) ) {
809 $modules[$moduleName] = $instance;
820 private function outputGeneralPageInfo() {
828 $values = $pageSet->getNormalizedTitlesAsResult( $result );
831 $fit = $fit && $result->addValue(
'query',
'normalized', $values );
833 $values = $pageSet->getConvertedTitlesAsResult( $result );
835 $fit = $fit && $result->addValue(
'query',
'converted', $values );
837 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams[
'iwurl'] );
839 $fit = $fit && $result->addValue(
'query',
'interwiki', $values );
841 $values = $pageSet->getRedirectTitlesAsResult( $result );
843 $fit = $fit && $result->addValue(
'query',
'redirects', $values );
845 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
847 $fit = $fit && $result->addValue(
'query',
'badrevids', $values );
855 foreach ( $pageSet->getMissingPages() as $fakeId => $page ) {
857 $vals[
'ns'] = $page->getNamespace();
858 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
859 $vals[
'missing'] =
true;
860 $title = $this->titleFactory->newFromPageIdentity( $page );
861 if ( $title->isKnown() ) {
862 $vals[
'known'] =
true;
864 $pages[$fakeId] = $vals;
867 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
868 $pages[$fakeId] = $data + [
'invalid' => true ];
871 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
879 foreach ( $pageSet->getSpecialPages() as $fakeId => $page ) {
881 $vals[
'ns'] = $page->getNamespace();
882 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
883 $vals[
'special'] =
true;
884 $title = $this->titleFactory->newFromPageReference( $page );
885 if ( !$title->isKnown() ) {
886 $vals[
'missing'] =
true;
888 $pages[$fakeId] = $vals;
892 foreach ( $pageSet->getGoodPages() as $pageid => $page ) {
894 $vals[
'pageid'] = $pageid;
895 $vals[
'ns'] = $page->getNamespace();
896 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
897 $pages[$pageid] = $vals;
900 if ( count( $pages ) ) {
901 $pageSet->populateGeneratorData( $pages );
904 if ( $this->mParams[
'indexpageids'] ) {
907 $pageIDs = array_map(
'strval', $pageIDs );
909 $fit = $fit && $result->addValue(
'query',
'pageids', $pageIDs );
913 $fit = $fit && $result->addValue(
'query',
'pages', $pages );
917 $this->
dieWithError(
'apierror-badconfig-resulttoosmall',
'badconfig' );
920 if ( $this->mParams[
'export'] ) {
921 $this->doExport( $pageSet, $result );
929 private function doExport( $pageSet, $result ) {
931 $titles = $pageSet->getGoodPages();
932 if ( count( $titles ) ) {
934 foreach ( $titles as $title ) {
935 if ( $this->
getAuthority()->authorizeRead(
'read', $title ) ) {
936 $exportTitles[] = $title;
941 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->
getDB() );
943 $exporter->setOutputSink( $sink );
944 $exporter->setSchemaVersion( $this->mParams[
'exportschema'] );
945 $exporter->openStream();
946 foreach ( $exportTitles as $title ) {
947 $exporter->pageByTitle( $title );
949 $exporter->closeStream();
954 if ( $this->mParams[
'exportnowrap'] ) {
969 ParamValidator::PARAM_ISMULTI =>
true,
970 ParamValidator::PARAM_TYPE =>
'submodule',
973 ParamValidator::PARAM_ISMULTI =>
true,
974 ParamValidator::PARAM_TYPE =>
'submodule',
977 ParamValidator::PARAM_ISMULTI =>
true,
978 ParamValidator::PARAM_TYPE =>
'submodule',
980 'indexpageids' =>
false,
982 'exportnowrap' =>
false,
991 'rawcontinue' =>
false,
994 $result += $this->
getPageSet()->getFinalParams( $flags );
1007 $allowedParams = [
'rawcontinue' => 1,
'indexpageids' => 1 ];
1011 $needed = $param ===
'meta';
1012 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
1020 $this->instantiateModules( $modules,
'meta' );
1021 foreach ( $modules as $module ) {
1022 if ( $module->isReadMode() ) {
1034 $this->instantiateModules( $modules,
'list' );
1035 $this->instantiateModules( $modules,
'meta' );
1036 $this->instantiateModules( $modules,
'prop' );
1037 foreach ( $modules as $module ) {
1038 if ( $module->isWriteMode() ) {
1047 $title = Title::newMainPage()->getPrefixedText();
1048 $mp = rawurlencode( $title );
1051 'action=query&prop=revisions&meta=siteinfo&' .
1052 "titles={$mp}&rvprop=user|comment&continue="
1053 =>
'apihelp-query-example-revisions',
1054 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
1055 =>
'apihelp-query-example-allpages',
1061 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
1062 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
1063 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
1064 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',