MediaWiki master
QueryPage.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\SpecialPage;
11
12use Exception;
56use RuntimeException;
57use stdClass;
66use Wikimedia\Timestamp\TimestampFormat as TS;
67
77abstract class QueryPage extends SpecialPage {
79 protected $listoutput = false;
80
82 protected $offset = 0;
83
85 protected $limit = 0;
86
94 protected $numRows;
95
99 protected $cachedTimestamp = null;
100
104 protected $shownavigation = true;
105
107 private $loadBalancer = null;
108
110 private $databaseProvider = null;
111
113 private $linkBatchFactory = null;
114
116 private $httpRequestFactory = null;
117
128 public static function getPages() {
129 static $qp = null;
130
131 if ( $qp === null ) {
132 $qp = [
133 [ SpecialAncientPages::class, 'Ancientpages' ],
134 [ SpecialBrokenRedirects::class, 'BrokenRedirects' ],
135 [ SpecialDeadendPages::class, 'Deadendpages' ],
136 [ SpecialDoubleRedirects::class, 'DoubleRedirects' ],
137 [ SpecialListDuplicatedFiles::class, 'ListDuplicatedFiles' ],
138 [ SpecialLinkSearch::class, 'LinkSearch' ],
139 [ SpecialListRedirects::class, 'Listredirects' ],
140 [ SpecialLonelyPages::class, 'Lonelypages' ],
141 [ SpecialLongPages::class, 'Longpages' ],
142 [ SpecialMediaStatistics::class, 'MediaStatistics', SpecialMediaStatistics::MAX_LIMIT ],
143 [ SpecialMIMESearch::class, 'MIMEsearch' ],
144 [ SpecialMostCategories::class, 'Mostcategories' ],
145 [ SpecialMostImages::class, 'Mostimages' ],
146 [ SpecialMostInterwikis::class, 'Mostinterwikis' ],
147 [ SpecialMostLinkedCategories::class, 'Mostlinkedcategories' ],
148 [ SpecialMostLinkedTemplates::class, 'Mostlinkedtemplates' ],
149 [ SpecialMostLinked::class, 'Mostlinked' ],
150 [ SpecialMostRevisions::class, 'Mostrevisions' ],
151 [ SpecialFewestRevisions::class, 'Fewestrevisions' ],
152 [ SpecialShortPages::class, 'Shortpages' ],
153 [ SpecialUncategorizedCategories::class, 'Uncategorizedcategories' ],
154 [ SpecialUncategorizedPages::class, 'Uncategorizedpages' ],
155 [ SpecialUncategorizedImages::class, 'Uncategorizedimages' ],
156 [ SpecialUncategorizedTemplates::class, 'Uncategorizedtemplates' ],
157 [ SpecialUnusedCategories::class, 'Unusedcategories' ],
158 [ SpecialUnusedImages::class, 'Unusedimages' ],
159 [ SpecialWantedCategories::class, 'Wantedcategories' ],
160 [ SpecialWantedFiles::class, 'Wantedfiles' ],
161 [ SpecialWantedPages::class, 'Wantedpages' ],
162 [ SpecialWantedTemplates::class, 'Wantedtemplates' ],
163 [ SpecialUnwatchedPages::class, 'Unwatchedpages' ],
164 [ SpecialUnusedTemplates::class, 'Unusedtemplates' ],
165 [ SpecialWithoutInterwiki::class, 'Withoutinterwiki' ],
166 ];
167 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onWgQueryPages( $qp );
168 }
169
170 return $qp;
171 }
172
177 final protected function setLinkBatchFactory( LinkBatchFactory $linkBatchFactory ) {
178 $this->linkBatchFactory = $linkBatchFactory;
179 }
180
185 final protected function getLinkBatchFactory(): LinkBatchFactory {
186 if ( $this->linkBatchFactory === null ) {
187 // Fallback if not provided
188 // TODO Change to wfWarn in a future release
189 $this->linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
190 }
191 return $this->linkBatchFactory;
192 }
193
197 private function getHttpRequestFactory(): HttpRequestFactory {
198 if ( $this->httpRequestFactory === null ) {
199 // Fallback if not provided
200 // TODO Do not rely on global state
201 $this->httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
202 }
203 return $this->httpRequestFactory;
204 }
205
211 public static function getDisabledQueryPages( Config $config ) {
212 $disableQueryPageUpdate = $config->get( MainConfigNames::DisableQueryPageUpdate );
213
214 if ( !is_array( $disableQueryPageUpdate ) ) {
215 return [];
216 }
217
218 $pages = [];
219 foreach ( $disableQueryPageUpdate as $name => $runMode ) {
220 if ( is_int( $name ) ) {
221 // The run mode may be omitted
222 $pages[$runMode] = 'disabled';
223 } else {
224 $pages[$name] = $runMode;
225 }
226 }
227 return $pages;
228 }
229
235 protected function setListoutput( $bool ) {
236 $this->listoutput = $bool;
237 }
238
262 abstract public function getQueryInfo();
263
272 protected function getOrderFields() {
273 return [ 'value' ];
274 }
275
287 public function usesTimestamps() {
288 return false;
289 }
290
297 protected function sortDescending() {
298 return true;
299 }
300
321 public function isExpensive() {
322 return $this->getConfig()->get( MainConfigNames::DisableQueryPages );
323 }
324
333 public function isCacheable() {
334 return true;
335 }
336
344 public function isCached() {
345 return $this->isExpensive() && $this->getConfig()->get( MainConfigNames::MiserMode );
346 }
347
354 public function isSyndicated() {
355 return true;
356 }
357
365 public function usesExternalSource(): bool {
366 $config = $this->getConfig();
367 $externalSources = $config->get( MainConfigNames::ExternalQuerySources );
368 $pageName = $this->getName();
369
370 return !empty( $externalSources[$pageName]['enabled'] ) &&
371 !empty( $externalSources[$pageName]['url'] );
372 }
373
383 abstract protected function formatResult( $skin, $result );
384
391 protected function getPageHeader() {
392 return '';
393 }
394
402 protected function showEmptyText() {
403 $this->getOutput()->addWikiMsg( 'specialpage-empty' );
404 }
405
414 protected function linkParameters() {
415 return [];
416 }
417
428 public function recache( $limit, $unused = true ) {
429 if ( !$this->isCacheable() ) {
430 return 0;
431 }
432
433 $fname = static::class . '::recache';
434 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
435
436 // Do query
437 $res = $this->reallyDoQuery( $limit, false );
438 $num = false;
439 if ( $res ) {
440 $num = $res->numRows();
441 // Fetch results
442 $vals = [];
443 foreach ( $res as $i => $row ) {
444 if ( isset( $row->value ) ) {
445 if ( $this->usesTimestamps() ) {
446 $value = (int)wfTimestamp( TS::UNIX, $row->value );
447 } else {
448 $value = intval( $row->value ); // T16414
449 }
450 } else {
451 $value = $i;
452 }
453
454 $vals[] = [
455 'qc_type' => $this->getName(),
456 'qc_namespace' => $row->namespace,
457 'qc_title' => $row->title,
458 'qc_value' => $value
459 ];
460 }
461
462 $dbw->doAtomicSection(
463 $fname,
464 function ( IDatabase $dbw, $fname ) {
465 // Clear out any old cached data
467 ->deleteFrom( 'querycache' )
468 ->where( [ 'qc_type' => $this->getName() ] )
469 ->caller( $fname )->execute();
470 // Update the querycache_info record for the page
472 ->insertInto( 'querycache_info' )
473 ->row( [ 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ] )
474 ->onDuplicateKeyUpdate()
475 ->uniqueIndexFields( [ 'qci_type' ] )
476 ->set( [ 'qci_timestamp' => $dbw->timestamp() ] )
477 ->caller( $fname )->execute();
478 }
479 );
480 // Save results into the querycache table on the primary DB
481 if ( count( $vals ) ) {
482 foreach ( array_chunk( $vals, 500 ) as $chunk ) {
484 ->insertInto( 'querycache' )
485 ->rows( $chunk )
486 ->caller( $fname )->execute();
487 }
488 }
489 }
490
491 return $num;
492 }
493
499 protected function getRecacheDB() {
500 return $this->getDatabaseProvider()->getReplicaDatabase( false, 'vslow' );
501 }
502
509 public function delete( LinkTarget $title ) {
510 if ( $this->isCached() ) {
511 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
512 $dbw->newDeleteQueryBuilder()
513 ->deleteFrom( 'querycache' )
514 ->where( [
515 'qc_type' => $this->getName(),
516 'qc_namespace' => $title->getNamespace(),
517 'qc_title' => $title->getDBkey(),
518 ] )
519 ->caller( __METHOD__ )->execute();
520 }
521 }
522
528 public function deleteAllCachedData() {
529 $fname = static::class . '::' . __FUNCTION__;
530 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
531 $dbw->newDeleteQueryBuilder()
532 ->deleteFrom( 'querycache' )
533 ->where( [ 'qc_type' => $this->getName() ] )
534 ->caller( $fname )->execute();
535 $dbw->newDeleteQueryBuilder()
536 ->deleteFrom( 'querycachetwo' )
537 ->where( [ 'qcc_type' => $this->getName() ] )
538 ->caller( $fname )->execute();
539 $dbw->newDeleteQueryBuilder()
540 ->deleteFrom( 'querycache_info' )
541 ->where( [ 'qci_type' => $this->getName() ] )
542 ->caller( $fname )->execute();
543 }
544
553 public function reallyDoQuery( $limit, $offset = false ) {
554 if ( $this->usesExternalSource() ) {
555 return $this->reallyDoQueryExternal( $limit, $offset );
556 }
557
558 return $this->reallyDoQueryInternal( $limit, $offset );
559 }
560
568 private function reallyDoQueryInternal( $limit, $offset = false ) {
569 $fname = static::class . '::reallyDoQueryInternal';
570 $dbr = $this->getRecacheDB();
571 $query = $this->getQueryInfo();
572 $order = $this->getOrderFields();
573
574 if ( $this->sortDescending() ) {
575 foreach ( $order as &$field ) {
576 $field .= ' DESC';
577 }
578 }
579
580 $tables = isset( $query['tables'] ) ? (array)$query['tables'] : [];
581 $fields = isset( $query['fields'] ) ? (array)$query['fields'] : [];
582 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : [];
583 $options = isset( $query['options'] ) ? (array)$query['options'] : [];
584 $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : [];
585
586 $queryBuilder = $dbr->newSelectQueryBuilder()
587 ->tables( $tables )
588 ->fields( $fields )
589 ->conds( $conds )
590 ->caller( $fname )
591 ->options( $options )
592 ->joinConds( $join_conds );
593 if ( $order ) {
594 $queryBuilder->orderBy( $order );
595 }
596
597 if ( $limit !== false ) {
598 $queryBuilder->limit( intval( $limit ) );
599 }
600
601 if ( $offset !== false ) {
602 $queryBuilder->offset( intval( $offset ) );
603 }
604
605 return $queryBuilder->fetchResultSet();
606 }
607
615 private function reallyDoQueryExternal( $limit, $offset = false ) {
616 $fname = static::class . '::reallyDoQueryExternal';
617 $httpRequestFactory = $this->getHttpRequestFactory();
618 $externalSources = $this->getConfig()->get( MainConfigNames::ExternalQuerySources );
619 $pageName = $this->getName();
620 $config = $externalSources[$pageName];
621
622 $options = [];
623 if ( isset( $config['timeout'] ) ) {
624 $options['timeout'] = (int)$config['timeout'];
625 }
626
627 $request = $httpRequestFactory->create( $config['url'], $options, $fname );
628
629 $status = $request->execute();
630 if ( !$status->isOK() ) {
631 throw new RuntimeException( "Failed to fetch data from external source '{$pageName}': " .
632 $status->getMessage()->text() );
633 }
634
635 $content = $request->getContent();
636 if ( $content === null || $content === '' ) {
637 throw new RuntimeException( "Empty response received from external source '{$pageName}'" );
638 }
639
640 $decoded = json_decode( $content, true );
641 if ( $decoded === null ) {
642 throw new RuntimeException( "Invalid JSON response from external source '{$pageName}': " .
643 json_last_error_msg() );
644 }
645
646 if ( !is_array( $decoded ) ) {
647 throw new RuntimeException( "Expected array data from external source '{$pageName}', got " .
648 gettype( $decoded ) );
649 }
650
651 $result = [];
652 foreach ( $decoded as $i => $row ) {
653 if ( !is_array( $row ) ) {
654 throw new RuntimeException( "Invalid row data at index {$i} from external source '{$pageName}': " .
655 'expected array, got ' . gettype( $row ) );
656 }
657
658 $requiredFields = [ 'qc_namespace', 'qc_title', 'qc_value' ];
659 foreach ( $requiredFields as $field ) {
660 if ( !array_key_exists( $field, $row ) ) {
661 throw new RuntimeException(
662 "Missing required field '{$field}' in row {$i} from external source '{$pageName}'" );
663 }
664 }
665
666 $result[] = (object)[
667 'namespace' => $row['qc_namespace'],
668 'title' => $row['qc_title'],
669 'value' => $row['qc_value']
670 ];
671 }
672
673 return new FakeResultWrapper( $result );
674 }
675
682 public function doQuery( $offset = false, $limit = false ) {
683 if ( $this->isCached() && $this->isCacheable() ) {
684 return $this->fetchFromCache( $limit, $offset );
685 } else {
686 return $this->reallyDoQuery( $limit, $offset );
687 }
688 }
689
699 public function fetchFromCache( $limit, $offset = false ) {
700 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
701 $queryBuilder = $dbr->newSelectQueryBuilder()
702 ->select( [ 'qc_type', 'namespace' => 'qc_namespace', 'title' => 'qc_title', 'value' => 'qc_value' ] )
703 ->from( 'querycache' )
704 ->where( [ 'qc_type' => $this->getName() ] );
705
706 if ( $limit !== false ) {
707 $queryBuilder->limit( intval( $limit ) );
708 }
709
710 if ( $offset !== false ) {
711 $queryBuilder->offset( intval( $offset ) );
712 }
713
714 $order = $this->getCacheOrderFields();
715 if ( $this->sortDescending() ) {
716 $queryBuilder->orderBy( $order, SelectQueryBuilder::SORT_DESC );
717 } else {
718 $queryBuilder->orderBy( $order );
719 }
720
721 return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
722 }
723
731 protected function getCacheOrderFields() {
732 return [ 'value' ];
733 }
734
738 public function getCachedTimestamp() {
739 if ( $this->cachedTimestamp === null ) {
740 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
741 $fname = static::class . '::getCachedTimestamp';
742 $this->cachedTimestamp = $dbr->newSelectQueryBuilder()
743 ->select( 'qci_timestamp' )
744 ->from( 'querycache_info' )
745 ->where( [ 'qci_type' => $this->getName() ] )
746 ->caller( $fname )->fetchField();
747 }
748 return $this->cachedTimestamp;
749 }
750
763 protected function getLimitOffset() {
764 [ $limit, $offset ] = $this->getRequest()
765 ->getLimitOffsetForUser( $this->getUser() );
766 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
767 $maxResults = $this->getMaxResults();
768 // Can't display more than max results on a page
769 $limit = min( $limit, $maxResults );
770 // Can't skip over more than the end of $maxResults
771 $offset = min( $offset, $maxResults + 1 );
772 }
773 return [ $limit, $offset ];
774 }
775
784 protected function getDBLimit( $uiLimit, $uiOffset ) {
785 $maxResults = $this->getMaxResults();
786 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
787 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
788 return max( $limit, 0 );
789 } else {
790 return $uiLimit + 1;
791 }
792 }
793
804 protected function getMaxResults() {
805 // Max of 10000, unless we store more than 10000 in query cache.
806 return max( $this->getConfig()->get( MainConfigNames::QueryCacheLimit ), 10000 );
807 }
808
815 public function execute( $par ) {
816 $this->checkPermissions();
817
818 $this->setHeaders();
819 $this->outputHeader();
820
821 $out = $this->getOutput();
822
823 if ( $this->isCached() && !$this->isCacheable() ) {
824 $out->addWikiMsg( 'querypage-disabled' );
825 return;
826 }
827
828 $out->setSyndicated( $this->isSyndicated() );
829
830 if ( $this->limit == 0 && $this->offset == 0 ) {
831 [ $this->limit, $this->offset ] = $this->getLimitOffset();
832 }
833 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
834 // @todo Use doQuery()
835 if ( !$this->isCached() ) {
836 // select one extra row for navigation
837 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
838 } else {
839 // Get the cached result, select one extra row for navigation
840 $res = $this->fetchFromCache( $dbLimit, $this->offset );
841 if ( !$this->listoutput ) {
842 // Fetch the timestamp of this update
843 $ts = $this->getCachedTimestamp();
844 $lang = $this->getLanguage();
845 $maxResults = $lang->formatNum( $this->getConfig()->get(
846 MainConfigNames::QueryCacheLimit ) );
847
848 if ( $ts ) {
849 $user = $this->getUser();
850 $updated = $lang->userTimeAndDate( $ts, $user );
851 $updateddate = $lang->userDate( $ts, $user );
852 $updatedtime = $lang->userTime( $ts, $user );
853 $out->addMeta( 'Data-Cache-Time', $ts );
854 $out->addJsConfigVars( 'dataCacheTime', $ts );
855 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
856 } else {
857 $out->addWikiMsg( 'perfcached', $maxResults );
858 }
859
860 // If updates on this page have been disabled, let the user know
861 // that the data set won't be refreshed for now
862 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
863 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
864 $runMode = $disabledQueryPages[$this->getName()];
865 if ( $runMode === 'disabled' ) {
866 $out->wrapWikiMsg(
867 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
868 'querypage-no-updates'
869 );
870 } else {
871 // Messages used here: querypage-updates-periodical
872 $out->wrapWikiMsg(
873 "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
874 'querypage-updates-' . $runMode
875 );
876 }
877 }
878 }
879 }
880
881 $this->numRows = $res->numRows();
882
883 $dbr = $this->getRecacheDB();
884 $this->preprocessResults( $dbr, $res );
885
886 $out->addHTML( Html::openElement( 'div', [ 'class' => 'mw-spcontent' ] ) );
887
888 // Top header and navigation
889 if ( $this->shownavigation ) {
890 $out->addHTML( $this->getPageHeader() );
891 if ( $this->numRows > 0 ) {
892 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams(
893 min( $this->numRows, $this->limit ), // do not show the one extra row, if exist
894 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
895 // Disable the "next" link when we reach the end
896 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
897 && ( $this->offset + $this->limit >= $this->getMaxResults() );
898 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
899 $paging = $this->buildPrevNextNavigation( $this->offset,
900 $this->limit, $this->linkParameters(), $atEnd, $par );
901 $out->addHTML( '<p>' . $paging . '</p>' );
902 } else {
903 // No results to show, so don't bother with "showing X of Y" etc.
904 // -- just let the user know and give up now
905 $this->showEmptyText();
906 $out->addHTML( Html::closeElement( 'div' ) );
907 return;
908 }
909 }
910
911 // The actual results; specialist subclasses will want to handle this
912 // with more than a straight list, so we hand them the info, plus
913 // an OutputPage, and let them get on with it
914 $this->outputResults( $out,
915 $this->getSkin(),
916 $dbr, // Should use IResultWrapper for this
917 $res,
918 min( $this->numRows, $this->limit ), // do not format the one extra row, if exist
919 $this->offset );
920
921 // Repeat the paging links at the bottom
922 if ( $this->shownavigation ) {
923 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here
924 $out->addHTML( '<p>' . $paging . '</p>' );
925 }
926
927 $out->addHTML( Html::closeElement( 'div' ) );
928 }
929
943 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
944 if ( $num > 0 ) {
945 $html = [];
946 if ( !$this->listoutput ) {
947 $html[] = $this->openList( $offset );
948 }
949
950 // $res might contain the whole 1,000 rows, so we read up to
951 // $num [should update this to use a Pager]
952 // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found
953 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
954 $line = $this->formatResult( $skin, $row );
955 if ( $line ) {
956 $html[] = $this->listoutput
957 ? $line
958 : "<li>{$line}</li>\n";
959 }
960 }
961
962 if ( !$this->listoutput ) {
963 $html[] = $this->closeList();
964 }
965
966 $html = $this->listoutput
967 ? $this->getContentLanguage()->listToText( $html )
968 : implode( '', $html );
969
970 $out->addHTML( $html );
971 }
972 }
973
978 protected function openList( $offset ) {
979 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
980 }
981
985 protected function closeList() {
986 return "</ol>\n";
987 }
988
995 protected function preprocessResults( $db, $res ) {
996 }
997
1010 protected function executeLBFromResultWrapper( IResultWrapper $res, $ns = null ) {
1011 if ( !$res->numRows() ) {
1012 return;
1013 }
1014
1015 $batch = $this->getLinkBatchFactory()->newLinkBatch()->setCaller( __METHOD__ );
1016 foreach ( $res as $row ) {
1017 $batch->add( $ns ?? (int)$row->namespace, $row->title );
1018 }
1019 $batch->execute();
1020
1021 $res->seek( 0 );
1022 }
1023
1029 final protected function setDBLoadBalancer( ILoadBalancer $loadBalancer ) {
1030 $this->loadBalancer = $loadBalancer;
1031 }
1032
1038 final protected function getDBLoadBalancer(): ILoadBalancer {
1039 if ( $this->loadBalancer === null ) {
1040 // Fallback if not provided
1041 // TODO Change to wfWarn in a future release
1042 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
1043 }
1044 return $this->loadBalancer;
1045 }
1046
1051 final protected function setDatabaseProvider( IConnectionProvider $databaseProvider ) {
1052 $this->databaseProvider = $databaseProvider;
1053 }
1054
1059 final protected function getDatabaseProvider(): IConnectionProvider {
1060 if ( $this->databaseProvider === null ) {
1061 $this->databaseProvider = MediaWikiServices::getInstance()->getConnectionProvider();
1062 }
1063 return $this->databaseProvider;
1064 }
1065}
1066
1068class_alias( QueryPage::class, 'QueryPage' );
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:68
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
Factory creating MWHttpRequest objects.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
This is one of the Core classes and should be read at least once by any new developers.
Factory for LinkBatch objects to batch query page metadata.
The base class for all skins.
Definition Skin.php:52
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
Definition QueryPage.php:77
getPageHeader()
The content returned by this function will be output before any result.
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array.
usesExternalSource()
Check if this query page is configured to fetch data from an external source via HTTP.
bool $shownavigation
Whether to show prev/next links.
bool $listoutput
Whether or not we want plain listoutput rather than an ordered list.
Definition QueryPage.php:79
getOrderFields()
Subclasses return an array of fields to order by here.
preprocessResults( $db, $res)
Do any necessary preprocessing of the result object.
setDatabaseProvider(IConnectionProvider $databaseProvider)
getLimitOffset()
Returns limit and offset, as returned by $this->getRequest()->getLimitOffsetForUser().
int $offset
The offset and limit in use, as passed to the query() function.
Definition QueryPage.php:82
getDBLimit( $uiLimit, $uiOffset)
What is limit to fetch from DB.
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
execute( $par)
This is the actual workhorse.
setListoutput( $bool)
A mutator for $this->listoutput;.
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
getMaxResults()
Get max number of results we can return in miser mode.
isExpensive()
Should this query page only be updated offline on large wikis?
static getPages()
Get a list of query page classes and their associated special pages, for periodic updates.
fetchFromCache( $limit, $offset=false)
Fetch the query results from the query cache.
setDBLoadBalancer(ILoadBalancer $loadBalancer)
string null false $cachedTimestamp
Definition QueryPage.php:99
deleteAllCachedData()
Remove all cached value This is needed when the page is no longer using the cache.
showEmptyText()
Outputs some kind of an informative message (via OutputPage) to let the user know that the query retu...
doQuery( $offset=false, $limit=false)
Somewhat deprecated, you probably want to be using execute()
outputResults( $out, $skin, $dbr, $res, $num, $offset)
Format and output report results using the given information plus OutputPage.
getRecacheDB()
Get a DB connection to be used for slow recache queries.
static getDisabledQueryPages(Config $config)
Get a list of disabled query pages and their run mode.
sortDescending()
Override to sort by increasing values.
int $numRows
The number of rows returned by the query.
Definition QueryPage.php:94
recache( $limit, $unused=true)
Clear the cache and save new results.
usesTimestamps()
Does this query return timestamps rather than integers in its 'value' field? If true,...
isSyndicated()
Sometimes we don't want to build rss / atom feeds.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
formatResult( $skin, $result)
Formats the results of the query for display.
getCacheOrderFields()
Return the order fields for fetchFromCache.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
reallyDoQuery( $limit, $offset=false)
Run the query and return the result.
Parent class for all special pages.
Implements Special:Ancientpages.
List of redirects to non-existent pages.
List of pages that contain no links to other pages.
List of redirects to another redirecting page.
List articles with the fewest revisions.
Special:LinkSearch to search the external-links table.
List all files where the current version is a duplicate of the current version of another file.
Lists all the redirecting pages on the wiki.
List of articles with no article linking to them, thus being lonely.
Implements Special:Longpages.
Search the database for files of the requested MIME type, comparing this with the 'img_major_mime' an...
List of pages that have the highest category count.
List of the most used images.
List of pages that have the highest interwiki count.
List of categories with the most pages in them.
List of templates with a large number of transclusion links, i.e.
List of pages ordered by the number of pages linking to them.
Implements Special:Mostrevisions.
List of the shortest pages in the database.
List of file pages which haven't been categorised.
List of all uncategorised pages in the Template namespace.
List of pages that are not on anyone's watchlist.
List of the most linked non-existent files.
List of the most-linked pages that do not exist.
List of pages without any language links.
Database error base class.
Definition DBError.php:22
Overloads the relevant methods of the real ResultWrapper so it doesn't go anywhere near an actual dat...
Build SELECT queries with a fluent interface.
Interface for configuration instances.
Definition Config.php:18
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Represents the target of a wiki link.
Provide primary and replica IDatabase connections.
Interface to a relational database.
Definition IDatabase.php:31
newDeleteQueryBuilder()
Get an DeleteQueryBuilder bound to this connection.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
This class is a delegate to ILBFactory for a given database cluster.
A database connection without write operations.
Result wrapper for grabbing data queried from an IDatabase object.
numRows()
Get the number of rows in a result object.
seek( $pos)
Change the position of the cursor in a result object.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...