48 private const QUERY_PROP_MODULES = [
50 'class' => ApiQueryCategories::class,
53 'class' => ApiQueryCategoryInfo::class,
56 'class' => ApiQueryContributors::class,
61 'GroupPermissionsLookup',
64 'deletedrevisions' => [
65 'class' => ApiQueryDeletedRevisions::class,
68 'ContentHandlerFactory',
81 'class' => ApiQueryDuplicateFiles::class,
87 'class' => ApiQueryExternalLinks::class,
93 'class' => ApiQueryBacklinksprop::class,
100 'class' => ApiQueryImages::class,
103 'class' => ApiQueryImageInfo::class,
112 'class' => ApiQueryInfo::class,
120 'LanguageConverterFactory',
124 'IntroMessageBuilder',
125 'PreloadedContentBuilder',
131 'class' => ApiQueryLinks::class,
139 'class' => ApiQueryBacklinksprop::class,
146 'class' => ApiQueryIWLinks::class,
152 'class' => ApiQueryLangLinks::class,
160 'class' => ApiQueryPageProps::class,
166 'class' => ApiQueryBacklinksprop::class,
173 'class' => ApiQueryRevisions::class,
176 'ContentHandlerFactory',
182 'ContentTransformer',
188 'stashimageinfo' => [
189 'class' => ApiQueryStashImageInfo::class,
198 'class' => ApiQueryLinks::class,
206 'class' => ApiQueryBacklinksprop::class,
217 private const QUERY_LIST_MODULES = [
219 'class' => ApiQueryAllCategories::class,
221 'alldeletedrevisions' => [
222 'class' => ApiQueryAllDeletedRevisions::class,
225 'ContentHandlerFactory',
231 'ContentTransformer',
238 'class' => ApiQueryAllLinks::class,
247 'class' => ApiQueryAllImages::class,
250 'GroupPermissionsLookup',
254 'class' => ApiQueryAllLinks::class,
263 'class' => ApiQueryAllPages::class,
271 'class' => ApiQueryAllLinks::class,
280 'class' => ApiQueryAllRevisions::class,
283 'ContentHandlerFactory',
289 'ContentTransformer',
295 'mystashedfiles' => [
296 'class' => ApiQueryMyStashedFiles::class,
298 'alltransclusions' => [
299 'class' => ApiQueryAllLinks::class,
308 'class' => ApiQueryAllUsers::class,
312 'GroupPermissionsLookup',
317 'class' => ApiQueryBacklinks::class,
323 'class' => ApiQueryBlocks::class,
326 'BlockRestrictionStore',
330 'categorymembers' => [
331 'class' => ApiQueryCategoryMembers::class,
337 'class' => ApiQueryDeletedrevs::class,
340 'RowCommentFormatter',
347 'class' => ApiQueryBacklinks::class,
353 'class' => ApiQueryExtLinksUsage::class,
359 'class' => ApiQueryFilearchive::class,
366 'class' => ApiQueryBacklinks::class,
372 'class' => ApiQueryIWBacklinks::class,
375 'class' => ApiQueryLangBacklinks::class,
378 'class' => ApiQueryLogEvents::class,
381 'RowCommentFormatter',
386 'class' => ApiQueryPagesWithProp::class,
389 'class' => ApiQueryPagePropNames::class,
392 'class' => ApiQueryPrefixSearch::class,
394 'SearchEngineConfig',
395 'SearchEngineFactory',
398 'protectedtitles' => [
399 'class' => ApiQueryProtectedTitles::class,
402 'RowCommentFormatter'
406 'class' => ApiQueryQueryPage::class,
408 'SpecialPageFactory',
412 'class' => ApiQueryRandom::class,
415 'class' => ApiQueryRecentChanges::class,
418 'RowCommentFormatter',
425 'class' => ApiQuerySearch::class,
427 'SearchEngineConfig',
428 'SearchEngineFactory',
433 'class' => ApiQueryTags::class,
439 'class' => ApiQueryUserContribs::class,
442 'UserIdentityLookup',
451 'class' => ApiQueryUsers::class,
461 'class' => ApiQueryWatchlist::class,
464 'WatchedItemQueryService',
472 'class' => ApiQueryWatchlistRaw::class,
474 'WatchedItemQueryService',
485 private const QUERY_META_MODULES = [
487 'class' => ApiQueryAllMessages::class,
496 'authmanagerinfo' => [
497 'class' => ApiQueryAuthManagerInfo::class,
503 'class' => ApiQuerySiteinfo::class,
507 'LanguageConverterFactory',
515 'SpecialPageFactory',
523 'class' => ApiQueryUserInfo::class,
525 'TalkPageNotificationManager',
533 'class' => ApiQueryFileRepoInfo::class,
539 'class' => ApiQueryTokens::class,
542 'class' => ApiQueryLanguageinfo::class,
547 'LanguageConverterFactory',
575 ObjectFactory $objectFactory,
580 parent::__construct( $main, $action );
589 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES,
'prop' );
590 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIPropModules ),
'prop' );
591 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES,
'list' );
592 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIListModules ),
'list' );
593 $this->mModuleMgr->addModules( self::QUERY_META_MODULES,
'meta' );
594 $this->mModuleMgr->addModules( $config->get( MainConfigNames::APIMetaModules ),
'meta' );
596 $this->
getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
600 $this->wikiExporterFactory = $wikiExporterFactory;
601 $this->titleFormatter = $titleFormatter;
602 $this->titleFactory = $titleFactory;
610 return $this->mModuleMgr;
618 return $this->mPageSet;
630 $this->
getMain()->createPrinterByName(
'xml' ) );
651 $this->instantiateModules( $allModules,
'prop' );
652 $propModules = array_keys( $allModules );
653 $this->instantiateModules( $allModules,
'list' );
654 $this->instantiateModules( $allModules,
'meta' );
660 $modules = $continuationManager->getRunModules();
661 '@phan-var ApiQueryBase[] $modules';
663 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
665 if ( !$continuationManager->isGeneratorDone() ) {
669 $t = microtime(
true );
670 $module->requestExtraData( $this->mPageSet );
671 $runTime = microtime(
true ) - $t;
675 'api-query.' . $module->getModuleName() .
'.extraDataTiming', 1000 * $runTime
679 $this->mPageSet->execute();
681 $this->outputGeneralPageInfo();
683 $this->mPageSet->executeDryRun();
686 $cacheMode = $this->mPageSet->getCacheMode();
690 $params = $module->extractRequestParams();
692 $cacheMode, $module->getCacheMode( $params ) );
694 $t = microtime(
true );
696 $runTime = microtime(
true ) - $t;
700 'api-query.' . $module->getModuleName() .
'.executeTiming', 1000 * $runTime
707 $this->
getMain()->setCacheMode( $cacheMode );
711 if ( $this->mParams[
'rawcontinue'] ) {
712 $data = $continuationManager->getRawNonContinuation();
714 $this->
getResult()->addValue(
null,
'query-noncontinue', $data,
715 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
717 $data = $continuationManager->getRawContinuation();
719 $this->
getResult()->addValue(
null,
'query-continue', $data,
720 ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
723 $continuationManager->setContinuationIntoResult( $this->
getResult() );
737 if ( $modCacheMode ===
'anon-public-user-private' ) {
738 if ( $cacheMode !==
'private' ) {
739 $cacheMode =
'anon-public-user-private';
741 } elseif ( $modCacheMode ===
'public' ) {
744 $cacheMode =
'private';
755 private function instantiateModules( &
$modules, $param ) {
756 $wasPosted = $this->
getRequest()->wasPosted();
757 if ( isset( $this->mParams[$param] ) ) {
758 foreach ( $this->mParams[$param] as $moduleName ) {
759 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
760 if ( $instance ===
null ) {
761 ApiBase::dieDebug( __METHOD__,
'Error instantiating module' );
763 if ( !$wasPosted && $instance->mustBePosted() ) {
767 if ( !array_key_exists( $moduleName,
$modules ) ) {
779 private function outputGeneralPageInfo() {
787 $values = $pageSet->getNormalizedTitlesAsResult( $result );
790 $fit = $fit && $result->addValue(
'query',
'normalized', $values );
792 $values = $pageSet->getConvertedTitlesAsResult( $result );
794 $fit = $fit && $result->addValue(
'query',
'converted', $values );
796 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams[
'iwurl'] );
798 $fit = $fit && $result->addValue(
'query',
'interwiki', $values );
800 $values = $pageSet->getRedirectTitlesAsResult( $result );
802 $fit = $fit && $result->addValue(
'query',
'redirects', $values );
804 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
806 $fit = $fit && $result->addValue(
'query',
'badrevids', $values );
814 foreach ( $pageSet->getMissingPages() as $fakeId => $page ) {
816 $vals[
'ns'] = $page->getNamespace();
817 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
818 $vals[
'missing'] =
true;
819 $title = $this->titleFactory->newFromPageIdentity( $page );
820 if ( $title->isKnown() ) {
821 $vals[
'known'] =
true;
823 $pages[$fakeId] = $vals;
826 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
827 $pages[$fakeId] = $data + [
'invalid' =>
true ];
830 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
838 foreach ( $pageSet->getSpecialPages() as $fakeId => $page ) {
840 $vals[
'ns'] = $page->getNamespace();
841 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
842 $vals[
'special'] =
true;
843 $title = $this->titleFactory->newFromPageReference( $page );
844 if ( !$title->isKnown() ) {
845 $vals[
'missing'] =
true;
847 $pages[$fakeId] = $vals;
851 foreach ( $pageSet->getGoodPages() as $pageid => $page ) {
853 $vals[
'pageid'] = $pageid;
854 $vals[
'ns'] = $page->getNamespace();
855 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
856 $pages[$pageid] = $vals;
859 if ( count( $pages ) ) {
860 $pageSet->populateGeneratorData( $pages );
863 if ( $this->mParams[
'indexpageids'] ) {
864 $pageIDs = array_keys( ApiResult::stripMetadataNonRecursive( $pages ) );
866 $pageIDs = array_map(
'strval', $pageIDs );
868 $fit = $fit && $result->addValue(
'query',
'pageids', $pageIDs );
872 $fit = $fit && $result->addValue(
'query',
'pages', $pages );
876 $this->
dieWithError(
'apierror-badconfig-resulttoosmall',
'badconfig' );
879 if ( $this->mParams[
'export'] ) {
880 $this->doExport( $pageSet, $result );
888 private function doExport( $pageSet, $result ) {
890 $titles = $pageSet->getGoodPages();
891 if ( count( $titles ) ) {
893 foreach ( $titles as $title ) {
894 if ( $this->
getAuthority()->authorizeRead(
'read', $title ) ) {
895 $exportTitles[] = $title;
900 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->
getDB() );
902 $exporter->setOutputSink( $sink );
903 $exporter->setSchemaVersion( $this->mParams[
'exportschema'] );
904 $exporter->openStream();
905 foreach ( $exportTitles as $title ) {
906 $exporter->pageByTitle( $title );
908 $exporter->closeStream();
913 if ( $this->mParams[
'exportnowrap'] ) {
916 $result->addValue(
null,
'text', $sink, ApiResult::NO_SIZE_CHECK );
917 $result->addValue(
null,
'mime',
'text/xml', ApiResult::NO_SIZE_CHECK );
918 $result->addValue(
null,
'filename',
'export.xml', ApiResult::NO_SIZE_CHECK );
920 $result->addValue(
'query',
'export', $sink, ApiResult::NO_SIZE_CHECK );
921 $result->addValue(
'query', ApiResult::META_BC_SUBELEMENTS, [
'export' ] );
928 ParamValidator::PARAM_ISMULTI =>
true,
929 ParamValidator::PARAM_TYPE =>
'submodule',
932 ParamValidator::PARAM_ISMULTI =>
true,
933 ParamValidator::PARAM_TYPE =>
'submodule',
936 ParamValidator::PARAM_ISMULTI =>
true,
937 ParamValidator::PARAM_TYPE =>
'submodule',
939 'indexpageids' =>
false,
941 'exportnowrap' =>
false,
943 ParamValidator::PARAM_DEFAULT => WikiExporter::schemaVersion(),
944 ParamValidator::PARAM_TYPE => XmlDumpWriter::$supportedSchemas,
950 'rawcontinue' =>
false,
953 $result += $this->
getPageSet()->getFinalParams( $flags );
966 $allowedParams = [
'rawcontinue' => 1,
'indexpageids' => 1 ];
970 $needed = $param ===
'meta';
971 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
979 $this->instantiateModules(
$modules,
'meta' );
981 if ( $module->isReadMode() ) {
990 $title = Title::newMainPage()->getPrefixedText();
991 $mp = rawurlencode( $title );
994 'action=query&prop=revisions&meta=siteinfo&' .
995 "titles={$mp}&rvprop=user|comment&continue="
996 =>
'apihelp-query-example-revisions',
997 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
998 =>
'apihelp-query-example-allpages',
1004 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
1005 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
1006 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
1007 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',