46 private const QUERY_PROP_MODULES = [
48 'class' => ApiQueryCategories::class,
51 'class' => ApiQueryCategoryInfo::class,
54 'class' => ApiQueryContributors::class,
59 'GroupPermissionsLookup',
62 'deletedrevisions' => [
63 'class' => ApiQueryDeletedRevisions::class,
66 'ContentHandlerFactory',
76 'class' => ApiQueryDuplicateFiles::class,
82 'class' => ApiQueryExternalLinks::class,
85 'class' => ApiQueryBacklinksprop::class,
92 'class' => ApiQueryImages::class,
95 'class' => ApiQueryImageInfo::class,
104 'class' => ApiQueryInfo::class,
112 'LanguageConverterFactory',
118 'class' => ApiQueryLinks::class,
126 'class' => ApiQueryBacklinksprop::class,
133 'class' => ApiQueryIWLinks::class,
136 'class' => ApiQueryLangLinks::class,
143 'class' => ApiQueryPageProps::class,
149 'class' => ApiQueryBacklinksprop::class,
156 'class' => ApiQueryRevisions::class,
159 'ContentHandlerFactory',
165 'ContentTransformer',
168 'stashimageinfo' => [
169 'class' => ApiQueryStashImageInfo::class,
178 'class' => ApiQueryLinks::class,
186 'class' => ApiQueryBacklinksprop::class,
197 private const QUERY_LIST_MODULES = [
199 'class' => ApiQueryAllCategories::class,
201 'alldeletedrevisions' => [
202 'class' => ApiQueryAllDeletedRevisions::class,
205 'ContentHandlerFactory',
211 'ContentTransformer',
215 'class' => ApiQueryAllLinks::class,
224 'class' => ApiQueryAllImages::class,
227 'GroupPermissionsLookup',
231 'class' => ApiQueryAllLinks::class,
240 'class' => ApiQueryAllPages::class,
248 'class' => ApiQueryAllLinks::class,
257 'class' => ApiQueryAllRevisions::class,
260 'ContentHandlerFactory',
266 'ContentTransformer',
269 'mystashedfiles' => [
270 'class' => ApiQueryMyStashedFiles::class,
272 'alltransclusions' => [
273 'class' => ApiQueryAllLinks::class,
282 'class' => ApiQueryAllUsers::class,
286 'GroupPermissionsLookup',
291 'class' => ApiQueryBacklinks::class,
297 'class' => ApiQueryBlocks::class,
300 'BlockRestrictionStore',
304 'categorymembers' => [
305 'class' => ApiQueryCategoryMembers::class,
311 'class' => ApiQueryDeletedrevs::class,
314 'RowCommentFormatter',
321 'class' => ApiQueryBacklinks::class,
327 'class' => ApiQueryExtLinksUsage::class,
330 'class' => ApiQueryFilearchive::class,
337 'class' => ApiQueryBacklinks::class,
343 'class' => ApiQueryIWBacklinks::class,
346 'class' => ApiQueryLangBacklinks::class,
349 'class' => ApiQueryLogEvents::class,
352 'RowCommentFormatter',
357 'class' => ApiQueryPagesWithProp::class,
360 'class' => ApiQueryPagePropNames::class,
363 'class' => ApiQueryPrefixSearch::class,
365 'SearchEngineConfig',
366 'SearchEngineFactory',
369 'protectedtitles' => [
370 'class' => ApiQueryProtectedTitles::class,
373 'RowCommentFormatter'
377 'class' => ApiQueryQueryPage::class,
379 'SpecialPageFactory',
383 'class' => ApiQueryRandom::class,
386 'class' => ApiQueryRecentChanges::class,
389 'RowCommentFormatter',
396 'class' => ApiQuerySearch::class,
398 'SearchEngineConfig',
399 'SearchEngineFactory',
403 'class' => ApiQueryTags::class,
406 'class' => ApiQueryUserContribs::class,
409 'UserIdentityLookup',
417 'class' => ApiQueryUsers::class,
427 'class' => ApiQueryWatchlist::class,
430 'WatchedItemQueryService',
437 'class' => ApiQueryWatchlistRaw::class,
439 'WatchedItemQueryService',
450 private const QUERY_META_MODULES = [
452 'class' => ApiQueryAllMessages::class,
461 'authmanagerinfo' => [
462 'class' => ApiQueryAuthManagerInfo::class,
468 'class' => ApiQuerySiteinfo::class,
472 'LanguageConverterFactory',
480 'SpecialPageFactory',
487 'class' => ApiQueryUserInfo::class,
489 'TalkPageNotificationManager',
497 'class' => ApiQueryFileRepoInfo::class,
503 'class' => ApiQueryTokens::class,
506 'class' => ApiQueryLanguageinfo::class,
511 'LanguageConverterFactory',
522 private $mNamedDB = [];
526 private $loadBalancer;
529 private $wikiExporterFactory;
541 ObjectFactory $objectFactory,
545 parent::__construct( $main, $action );
554 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES,
'prop' );
555 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIPropModules ),
'prop' );
556 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES,
'list' );
557 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIListModules ),
'list' );
558 $this->mModuleMgr->addModules( self::QUERY_META_MODULES,
'meta' );
559 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIMetaModules ),
'meta' );
561 $this->
getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
565 $this->loadBalancer = $loadBalancer;
566 $this->wikiExporterFactory = $wikiExporterFactory;
574 return $this->mModuleMgr;
593 if ( !array_key_exists( $name, $this->mNamedDB ) ) {
594 $this->mNamedDB[$name] = $this->loadBalancer->getConnectionRef( $db, $groups );
597 return $this->mNamedDB[$name];
605 return $this->mPageSet;
617 $this->
getMain()->createPrinterByName(
'xml' ) );
638 $this->instantiateModules( $allModules,
'prop' );
639 $propModules = array_keys( $allModules );
640 $this->instantiateModules( $allModules,
'list' );
641 $this->instantiateModules( $allModules,
'meta' );
647 $modules = $continuationManager->getRunModules();
648 '@phan-var ApiQueryBase[] $modules';
650 if ( !$continuationManager->isGeneratorDone() ) {
654 $module->requestExtraData( $this->mPageSet );
657 $this->mPageSet->execute();
659 $this->outputGeneralPageInfo();
661 $this->mPageSet->executeDryRun();
664 $cacheMode = $this->mPageSet->getCacheMode();
668 $params = $module->extractRequestParams();
670 $cacheMode, $module->getCacheMode( $params ) );
676 $this->
getMain()->setCacheMode( $cacheMode );
680 if ( $this->mParams[
'rawcontinue'] ) {
681 $data = $continuationManager->getRawNonContinuation();
683 $this->
getResult()->addValue(
null,
'query-noncontinue', $data,
684 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
686 $data = $continuationManager->getRawContinuation();
688 $this->
getResult()->addValue(
null,
'query-continue', $data,
689 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
692 $continuationManager->setContinuationIntoResult( $this->
getResult() );
706 if ( $modCacheMode ===
'anon-public-user-private' ) {
707 if ( $cacheMode !==
'private' ) {
708 $cacheMode =
'anon-public-user-private';
710 } elseif ( $modCacheMode ===
'public' ) {
713 $cacheMode =
'private';
724 private function instantiateModules( &
$modules, $param ) {
725 $wasPosted = $this->
getRequest()->wasPosted();
726 if ( isset( $this->mParams[$param] ) ) {
727 foreach ( $this->mParams[$param] as $moduleName ) {
728 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
729 if ( $instance ===
null ) {
730 ApiBase::dieDebug( __METHOD__,
'Error instantiating module' );
732 if ( !$wasPosted && $instance->mustBePosted() ) {
736 if ( !array_key_exists( $moduleName,
$modules ) ) {
748 private function outputGeneralPageInfo() {
756 $values = $pageSet->getNormalizedTitlesAsResult( $result );
759 $fit = $fit && $result->addValue(
'query',
'normalized', $values );
761 $values = $pageSet->getConvertedTitlesAsResult( $result );
763 $fit = $fit && $result->addValue(
'query',
'converted', $values );
765 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams[
'iwurl'] );
767 $fit = $fit && $result->addValue(
'query',
'interwiki', $values );
769 $values = $pageSet->getRedirectTitlesAsResult( $result );
771 $fit = $fit && $result->addValue(
'query',
'redirects', $values );
773 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
775 $fit = $fit && $result->addValue(
'query',
'badrevids', $values );
782 foreach ( $pageSet->getMissingTitles() as $fakeId =>
$title ) {
785 $vals[
'missing'] =
true;
786 if (
$title->isKnown() ) {
787 $vals[
'known'] =
true;
789 $pages[$fakeId] = $vals;
792 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
793 $pages[$fakeId] = $data + [
'invalid' =>
true ];
796 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
804 foreach ( $pageSet->getSpecialTitles() as $fakeId =>
$title ) {
807 $vals[
'special'] =
true;
808 if ( !
$title->isKnown() ) {
809 $vals[
'missing'] =
true;
811 $pages[$fakeId] = $vals;
815 foreach ( $pageSet->getGoodTitles() as $pageid =>
$title ) {
817 $vals[
'pageid'] = $pageid;
819 $pages[$pageid] = $vals;
822 if ( count( $pages ) ) {
823 $pageSet->populateGeneratorData( $pages );
826 if ( $this->mParams[
'indexpageids'] ) {
827 $pageIDs = array_keys( ApiResult::stripMetadataNonRecursive( $pages ) );
829 $pageIDs = array_map(
'strval', $pageIDs );
831 $fit = $fit && $result->addValue(
'query',
'pageids', $pageIDs );
835 $fit = $fit && $result->addValue(
'query',
'pages', $pages );
839 $this->
dieWithError(
'apierror-badconfig-resulttoosmall',
'badconfig' );
842 if ( $this->mParams[
'export'] ) {
843 $this->doExport( $pageSet, $result );
851 private function doExport( $pageSet, $result ) {
853 $titles = $pageSet->getGoodPages();
854 if ( count( $titles ) ) {
856 foreach ( $titles as
$title ) {
863 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->
getDB() );
865 $exporter->setOutputSink( $sink );
866 $exporter->setSchemaVersion( $this->mParams[
'exportschema'] );
867 $exporter->openStream();
868 foreach ( $exportTitles as
$title ) {
869 $exporter->pageByTitle(
$title );
871 $exporter->closeStream();
876 if ( $this->mParams[
'exportnowrap'] ) {
879 $result->addValue(
null,
'text', $sink, ApiResult::NO_SIZE_CHECK );
880 $result->addValue(
null,
'mime',
'text/xml', ApiResult::NO_SIZE_CHECK );
881 $result->addValue(
null,
'filename',
'export.xml', ApiResult::NO_SIZE_CHECK );
883 $result->addValue(
'query',
'export', $sink, ApiResult::NO_SIZE_CHECK );
884 $result->addValue(
'query', ApiResult::META_BC_SUBELEMENTS, [
'export' ] );
891 ParamValidator::PARAM_ISMULTI =>
true,
892 ParamValidator::PARAM_TYPE =>
'submodule',
895 ParamValidator::PARAM_ISMULTI =>
true,
896 ParamValidator::PARAM_TYPE =>
'submodule',
899 ParamValidator::PARAM_ISMULTI =>
true,
900 ParamValidator::PARAM_TYPE =>
'submodule',
902 'indexpageids' =>
false,
904 'exportnowrap' =>
false,
906 ParamValidator::PARAM_DEFAULT => WikiExporter::schemaVersion(),
907 ParamValidator::PARAM_TYPE => XmlDumpWriter::$supportedSchemas,
913 'rawcontinue' =>
false,
916 $result += $this->
getPageSet()->getFinalParams( $flags );
929 $allowedParams = [
'rawcontinue' => 1,
'indexpageids' => 1 ];
933 $needed = $param ===
'meta';
934 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
942 $this->instantiateModules(
$modules,
'meta' );
944 if ( $module->isReadMode() ) {
953 $title = Title::newMainPage()->getPrefixedText();
954 $mp = rawurlencode(
$title );
957 'action=query&prop=revisions&meta=siteinfo&' .
958 "titles={$mp}&rvprop=user|comment&continue="
959 =>
'apihelp-query-example-revisions',
960 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
961 =>
'apihelp-query-example-allpages',
967 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
968 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
969 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
970 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',