41 private const QUERY_PROP_MODULES = [
43 'class' => ApiQueryCategories::class,
46 'class' => ApiQueryCategoryInfo::class,
49 'class' => ApiQueryContributors::class,
54 'GroupPermissionsLookup',
58 'deletedrevisions' => [
59 'class' => ApiQueryDeletedRevisions::class,
62 'ContentHandlerFactory',
76 'class' => ApiQueryDuplicateFiles::class,
82 'class' => ApiQueryExternalLinks::class,
88 'class' => ApiQueryBacklinksprop::class,
95 'class' => ApiQueryImages::class,
98 'class' => ApiQueryImageInfo::class,
107 'class' => ApiQueryInfo::class,
115 'LanguageConverterFactory',
120 'IntroMessageBuilder',
121 'PreloadedContentBuilder',
128 'class' => ApiQueryLinks::class,
136 'class' => ApiQueryBacklinksprop::class,
143 'class' => ApiQueryIWLinks::class,
149 'class' => ApiQueryLangLinks::class,
157 'class' => ApiQueryPageProps::class,
163 'class' => ApiQueryBacklinksprop::class,
170 'class' => ApiQueryRevisions::class,
173 'ContentHandlerFactory',
180 'ContentTransformer',
187 'stashimageinfo' => [
188 'class' => ApiQueryStashImageInfo::class,
197 'class' => ApiQueryLinks::class,
205 'class' => ApiQueryBacklinksprop::class,
216 private const QUERY_LIST_MODULES = [
218 'class' => ApiQueryAllCategories::class,
220 'alldeletedrevisions' => [
221 'class' => ApiQueryAllDeletedRevisions::class,
224 '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',
290 'ContentTransformer',
296 'mystashedfiles' => [
297 'class' => ApiQueryMyStashedFiles::class,
299 'alltransclusions' => [
300 'class' => ApiQueryAllLinks::class,
309 'class' => ApiQueryAllUsers::class,
313 'GroupPermissionsLookup',
316 'RecentChangeLookup',
317 'TempUserDetailsLookup',
321 'class' => ApiQueryBacklinks::class,
327 'class' => ApiQueryBlocks::class,
329 'DatabaseBlockStore',
331 'BlockRestrictionStore',
337 'categorymembers' => [
338 'class' => ApiQueryCategoryMembers::class,
344 'class' => ApiQueryCodexIcons::class,
347 'class' => ApiQueryDeletedrevs::class,
350 'RowCommentFormatter',
358 'class' => ApiQueryBacklinks::class,
364 'class' => ApiQueryExtLinksUsage::class,
370 'class' => ApiQueryFilearchive::class,
377 'class' => ApiQueryBacklinks::class,
383 'class' => ApiQueryIWBacklinks::class,
386 'class' => ApiQueryLangBacklinks::class,
389 'class' => ApiQueryLogEvents::class,
392 'RowCommentFormatter',
396 'LogFormatterFactory',
400 'class' => ApiQueryPagesWithProp::class,
403 'class' => ApiQueryPagePropNames::class,
406 'class' => ApiQueryPrefixSearch::class,
408 'SearchEngineConfig',
409 'SearchEngineFactory',
412 'protectedtitles' => [
413 'class' => ApiQueryProtectedTitles::class,
416 'RowCommentFormatter'
420 'class' => ApiQueryQueryPage::class,
422 'SpecialPageFactory',
426 'class' => ApiQueryRandom::class,
428 'ContentHandlerFactory'
432 'class' => ApiQueryRecentChanges::class,
435 'RowCommentFormatter',
438 'LogFormatterFactory',
439 'ChangesListQueryFactory',
440 'RecentChangeLookup',
444 'class' => ApiQuerySearch::class,
446 'SearchEngineConfig',
447 'SearchEngineFactory',
452 'class' => ApiQueryTags::class,
457 'trackingcategories' => [
458 'class' => ApiQueryTrackingCategories::class,
460 'TrackingCategories',
464 'class' => ApiQueryUserContribs::class,
467 'UserIdentityLookup',
477 'class' => ApiQueryUsers::class,
485 'TempUserDetailsLookup',
489 'class' => ApiQueryWatchlist::class,
492 'ChangesListQueryFactory',
493 'RowCommentFormatter',
495 'LogFormatterFactory',
496 'RecentChangeLookup',
501 'class' => ApiQueryWatchlistRaw::class,
503 'WatchedItemQueryService',
514 private const QUERY_META_MODULES = [
516 'class' => ApiQueryAllMessages::class,
525 'authmanagerinfo' => [
526 'class' => ApiQueryAuthManagerInfo::class,
532 'class' => ApiQuerySiteinfo::class,
537 'LanguageConverterFactory',
545 'SpecialPageFactory',
551 'GroupPermissionsLookup',
555 'class' => ApiQueryUserInfo::class,
557 'TalkPageNotificationManager',
565 'class' => ApiQueryFileRepoInfo::class,
571 'class' => ApiQueryTokens::class,
574 'class' => ApiQueryLanguageinfo::class,
579 'LanguageConverterFactory',
601 ObjectFactory $objectFactory,
606 parent::__construct( $main, $action );
615 $this->mModuleMgr->addModules( self::QUERY_PROP_MODULES,
'prop' );
617 $this->mModuleMgr->addModules( self::QUERY_LIST_MODULES,
'list' );
619 $this->mModuleMgr->addModules( self::QUERY_META_MODULES,
'meta' );
622 $this->
getHookRunner()->onApiQuery__moduleManager( $this->mModuleMgr );
626 $this->wikiExporterFactory = $wikiExporterFactory;
627 $this->titleFormatter = $titleFormatter;
628 $this->titleFactory = $titleFactory;
636 return $this->mModuleMgr;
644 return $this->mPageSet;
656 $this->
getMain()->createPrinterByName(
'xml' ) );
677 $this->instantiateModules( $allModules,
'prop' );
678 $propModules = array_keys( $allModules );
679 $this->instantiateModules( $allModules,
'list' );
680 $this->instantiateModules( $allModules,
'meta' );
686 $modules = $continuationManager->getRunModules();
687 '@phan-var ApiQueryBase[] $modules';
690 $message =
'hookaborted';
691 if ( !$this->
getHookRunner()->onApiQueryCheckCanExecute( $modules, $this->
getUser(), $message ) ) {
697 if ( !$continuationManager->isGeneratorDone() ) {
700 foreach ( $modules as $module ) {
702 $timer = $statsFactory->getTiming(
'api_query_extraDataTiming_seconds' )
703 ->setLabel(
'module', $module->getModuleName() )
705 $module->requestExtraData( $this->mPageSet );
709 $this->mPageSet->execute();
711 $this->outputGeneralPageInfo();
713 $this->mPageSet->executeDryRun();
716 $cacheMode = $this->mPageSet->getCacheMode();
719 foreach ( $modules as $module ) {
721 $timer = $statsFactory->getTiming(
'api_query_executeTiming_seconds' )
722 ->setLabel(
'module', $module->getModuleName() )
724 $t = microtime(
true );
726 $params = $module->extractRequestParams();
728 $cacheMode, $module->getCacheMode( $params ) );
729 $scope = LoggerFactory::getContext()->addScoped( [
730 'context.api_query_module_name' => $module->getModuleName(),
736 $module->recordUnifiedMetrics(
737 microtime(
true ) - $t
739 }
catch ( \Throwable $e ) {
741 $module->recordUnifiedMetrics(
742 microtime(
true ) - $t,
744 'status' =>
'error_' . $e->getCode(),
750 ScopedCallback::consume( $scope );
758 $this->
getMain()->setCacheMode( $cacheMode );
762 if ( $this->mParams[
'rawcontinue'] ) {
763 $data = $continuationManager->getRawNonContinuation();
765 $this->
getResult()->addValue(
null,
'query-noncontinue', $data,
768 $data = $continuationManager->getRawContinuation();
770 $this->
getResult()->addValue(
null,
'query-continue', $data,
774 $continuationManager->setContinuationIntoResult( $this->
getResult() );
788 if ( $modCacheMode ===
'anon-public-user-private' ) {
789 if ( $cacheMode !==
'private' ) {
790 $cacheMode =
'anon-public-user-private';
792 } elseif ( $modCacheMode ===
'public' ) {
795 $cacheMode =
'private';
806 private function instantiateModules( &$modules, $param ) {
807 $wasPosted = $this->
getRequest()->wasPosted();
808 if ( isset( $this->mParams[$param] ) ) {
809 foreach ( $this->mParams[$param] as $moduleName ) {
810 $instance = $this->mModuleMgr->getModule( $moduleName, $param );
811 if ( $instance ===
null ) {
814 if ( !$wasPosted && $instance->mustBePosted() ) {
818 if ( !array_key_exists( $moduleName, $modules ) ) {
819 $modules[$moduleName] = $instance;
830 private function outputGeneralPageInfo() {
838 $values = $pageSet->getNormalizedTitlesAsResult( $result );
841 $fit = $fit && $result->addValue(
'query',
'normalized', $values );
843 $values = $pageSet->getConvertedTitlesAsResult( $result );
845 $fit = $fit && $result->addValue(
'query',
'converted', $values );
847 $values = $pageSet->getInterwikiTitlesAsResult( $result, $this->mParams[
'iwurl'] );
849 $fit = $fit && $result->addValue(
'query',
'interwiki', $values );
851 $values = $pageSet->getRedirectTitlesAsResult( $result );
853 $fit = $fit && $result->addValue(
'query',
'redirects', $values );
855 $values = $pageSet->getMissingRevisionIDsAsResult( $result );
857 $fit = $fit && $result->addValue(
'query',
'badrevids', $values );
865 foreach ( $pageSet->getMissingPages() as $fakeId => $page ) {
867 $vals[
'ns'] = $page->getNamespace();
868 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
869 $vals[
'missing'] =
true;
870 $title = $this->titleFactory->newFromPageIdentity( $page );
871 if ( $title->isKnown() ) {
872 $vals[
'known'] =
true;
874 $pages[$fakeId] = $vals;
877 foreach ( $pageSet->getInvalidTitlesAndReasons() as $fakeId => $data ) {
878 $pages[$fakeId] = $data + [
'invalid' => true ];
881 foreach ( $pageSet->getMissingPageIDs() as $pageid ) {
889 foreach ( $pageSet->getSpecialPages() as $fakeId => $page ) {
891 $vals[
'ns'] = $page->getNamespace();
892 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
893 $vals[
'special'] =
true;
894 $title = $this->titleFactory->newFromPageReference( $page );
895 if ( !$title->isKnown() ) {
896 $vals[
'missing'] =
true;
898 $pages[$fakeId] = $vals;
902 foreach ( $pageSet->getGoodPages() as $pageid => $page ) {
904 $vals[
'pageid'] = $pageid;
905 $vals[
'ns'] = $page->getNamespace();
906 $vals[
'title'] = $this->titleFormatter->getPrefixedText( $page );
907 $pages[$pageid] = $vals;
910 if ( count( $pages ) ) {
911 $pageSet->populateGeneratorData( $pages );
914 if ( $this->mParams[
'indexpageids'] ) {
917 $pageIDs = array_map(
'strval', $pageIDs );
919 $fit = $fit && $result->addValue(
'query',
'pageids', $pageIDs );
923 $fit = $fit && $result->addValue(
'query',
'pages', $pages );
927 $this->
dieWithError(
'apierror-badconfig-resulttoosmall',
'badconfig' );
930 if ( $this->mParams[
'export'] ) {
931 $this->doExport( $pageSet, $result );
939 private function doExport( $pageSet, $result ) {
941 $titles = $pageSet->getGoodPages();
942 if ( count( $titles ) ) {
944 foreach ( $titles as $title ) {
945 if ( $this->
getAuthority()->authorizeRead(
'read', $title ) ) {
946 $exportTitles[] = $title;
951 $exporter = $this->wikiExporterFactory->getWikiExporter( $this->
getDB() );
953 $exporter->setOutputSink( $sink );
954 $exporter->setSchemaVersion( $this->mParams[
'exportschema'] );
955 $exporter->openStream();
956 foreach ( $exportTitles as $title ) {
957 $exporter->pageByTitle( $title );
959 $exporter->closeStream();
964 if ( $this->mParams[
'exportnowrap'] ) {
980 ParamValidator::PARAM_ISMULTI =>
true,
981 ParamValidator::PARAM_TYPE =>
'submodule',
984 ParamValidator::PARAM_ISMULTI =>
true,
985 ParamValidator::PARAM_TYPE =>
'submodule',
988 ParamValidator::PARAM_ISMULTI =>
true,
989 ParamValidator::PARAM_TYPE =>
'submodule',
991 'indexpageids' =>
false,
993 'exportnowrap' =>
false,
1002 'rawcontinue' =>
false,
1005 $result += $this->
getPageSet()->getFinalParams( $flags );
1019 $allowedParams = [
'rawcontinue' => 1,
'indexpageids' => 1 ];
1023 $needed = $param ===
'meta';
1024 if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
1032 $this->instantiateModules( $modules,
'meta' );
1033 foreach ( $modules as $module ) {
1034 if ( $module->isReadMode() ) {
1047 $this->instantiateModules( $modules,
'list' );
1048 $this->instantiateModules( $modules,
'meta' );
1049 $this->instantiateModules( $modules,
'prop' );
1050 foreach ( $modules as $module ) {
1051 if ( $module->isWriteMode() ) {
1061 $title = Title::newMainPage()->getPrefixedText();
1062 $mp = rawurlencode( $title );
1065 'action=query&prop=revisions&meta=siteinfo&' .
1066 "titles={$mp}&rvprop=user|comment&continue="
1067 =>
'apihelp-query-example-revisions',
1068 'action=query&generator=allpages&gapprefix=API/&prop=revisions&continue='
1069 =>
'apihelp-query-example-allpages',
1076 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Query',
1077 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Meta',
1078 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Properties',
1079 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Lists',