MediaWiki master
QueryPage.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\SpecialPage;
25
26use Exception;
27use LogicException;
70use Skin;
71use stdClass;
79
89abstract class QueryPage extends SpecialPage {
91 protected $listoutput = false;
92
94 protected $offset = 0;
95
97 protected $limit = 0;
98
106 protected $numRows;
107
111 protected $cachedTimestamp = null;
112
116 protected $shownavigation = true;
117
119 private $loadBalancer = null;
120
122 private $databaseProvider = null;
123
125 private $linkBatchFactory = null;
126
137 public static function getPages() {
138 static $qp = null;
139
140 if ( $qp === null ) {
141 $qp = [
142 [ SpecialAncientPages::class, 'Ancientpages' ],
143 [ SpecialBrokenRedirects::class, 'BrokenRedirects' ],
144 [ SpecialDeadendPages::class, 'Deadendpages' ],
145 [ SpecialDoubleRedirects::class, 'DoubleRedirects' ],
146 [ SpecialListDuplicatedFiles::class, 'ListDuplicatedFiles' ],
147 [ SpecialLinkSearch::class, 'LinkSearch' ],
148 [ SpecialListRedirects::class, 'Listredirects' ],
149 [ SpecialLonelyPages::class, 'Lonelypages' ],
150 [ SpecialLongPages::class, 'Longpages' ],
151 [ SpecialMediaStatistics::class, 'MediaStatistics', SpecialMediaStatistics::MAX_LIMIT ],
152 [ SpecialMIMESearch::class, 'MIMEsearch' ],
153 [ SpecialMostCategories::class, 'Mostcategories' ],
154 [ SpecialMostImages::class, 'Mostimages' ],
155 [ SpecialMostInterwikis::class, 'Mostinterwikis' ],
156 [ SpecialMostLinkedCategories::class, 'Mostlinkedcategories' ],
157 [ SpecialMostLinkedTemplates::class, 'Mostlinkedtemplates' ],
158 [ SpecialMostLinked::class, 'Mostlinked' ],
159 [ SpecialMostRevisions::class, 'Mostrevisions' ],
160 [ SpecialFewestRevisions::class, 'Fewestrevisions' ],
161 [ SpecialShortPages::class, 'Shortpages' ],
162 [ SpecialUncategorizedCategories::class, 'Uncategorizedcategories' ],
163 [ SpecialUncategorizedPages::class, 'Uncategorizedpages' ],
164 [ SpecialUncategorizedImages::class, 'Uncategorizedimages' ],
165 [ SpecialUncategorizedTemplates::class, 'Uncategorizedtemplates' ],
166 [ SpecialUnusedCategories::class, 'Unusedcategories' ],
167 [ SpecialUnusedImages::class, 'Unusedimages' ],
168 [ SpecialWantedCategories::class, 'Wantedcategories' ],
169 [ SpecialWantedFiles::class, 'Wantedfiles' ],
170 [ SpecialWantedPages::class, 'Wantedpages' ],
171 [ SpecialWantedTemplates::class, 'Wantedtemplates' ],
172 [ SpecialUnwatchedPages::class, 'Unwatchedpages' ],
173 [ SpecialUnusedTemplates::class, 'Unusedtemplates' ],
174 [ SpecialWithoutInterwiki::class, 'Withoutinterwiki' ],
175 ];
176 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onWgQueryPages( $qp );
177 }
178
179 return $qp;
180 }
181
186 final protected function setLinkBatchFactory( LinkBatchFactory $linkBatchFactory ) {
187 $this->linkBatchFactory = $linkBatchFactory;
188 }
189
194 final protected function getLinkBatchFactory(): LinkBatchFactory {
195 if ( $this->linkBatchFactory === null ) {
196 // Fallback if not provided
197 // TODO Change to wfWarn in a future release
198 $this->linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
199 }
200 return $this->linkBatchFactory;
201 }
202
208 public static function getDisabledQueryPages( Config $config ) {
209 $disableQueryPageUpdate = $config->get( MainConfigNames::DisableQueryPageUpdate );
210
211 if ( !is_array( $disableQueryPageUpdate ) ) {
212 return [];
213 }
214
215 $pages = [];
216 foreach ( $disableQueryPageUpdate as $name => $runMode ) {
217 if ( is_int( $name ) ) {
218 // The run mode may be omitted
219 $pages[$runMode] = 'disabled';
220 } else {
221 $pages[$name] = $runMode;
222 }
223 }
224 return $pages;
225 }
226
232 protected function setListoutput( $bool ) {
233 $this->listoutput = $bool;
234 }
235
263 public function getQueryInfo() {
264 // @phan-suppress-next-line PhanTypeMismatchReturnProbablyReal null needed for b/c checks
265 return null;
266 }
267
274 protected function getSQL() {
275 wfDeprecated( __METHOD__, '1.39' );
276 throw new LogicException( "Bug in a QueryPage: doesn't implement getQueryInfo() nor "
277 . "getQuery() properly" );
278 }
279
288 protected function getOrderFields() {
289 return [ 'value' ];
290 }
291
303 public function usesTimestamps() {
304 return false;
305 }
306
313 protected function sortDescending() {
314 return true;
315 }
316
337 public function isExpensive() {
338 return $this->getConfig()->get( MainConfigNames::DisableQueryPages );
339 }
340
349 public function isCacheable() {
350 return true;
351 }
352
360 public function isCached() {
361 return $this->isExpensive() && $this->getConfig()->get( MainConfigNames::MiserMode );
362 }
363
370 public function isSyndicated() {
371 return true;
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 ) use ( $vals ) {
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->getDBLoadBalancer()
501 ->getConnection( ILoadBalancer::DB_REPLICA, 'vslow' );
502 }
503
510 public function delete( LinkTarget $title ) {
511 if ( $this->isCached() ) {
512 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
513 $dbw->newDeleteQueryBuilder()
514 ->deleteFrom( 'querycache' )
515 ->where( [
516 'qc_type' => $this->getName(),
517 'qc_namespace' => $title->getNamespace(),
518 'qc_title' => $title->getDBkey(),
519 ] )
520 ->caller( __METHOD__ )->execute();
521 }
522 }
523
529 public function deleteAllCachedData() {
530 $fname = static::class . '::' . __FUNCTION__;
531 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
532 $dbw->newDeleteQueryBuilder()
533 ->deleteFrom( 'querycache' )
534 ->where( [ 'qc_type' => $this->getName() ] )
535 ->caller( $fname )->execute();
536 $dbw->newDeleteQueryBuilder()
537 ->deleteFrom( 'querycachetwo' )
538 ->where( [ 'qcc_type' => $this->getName() ] )
539 ->caller( $fname )->execute();
540 $dbw->newDeleteQueryBuilder()
541 ->deleteFrom( 'querycache_info' )
542 ->where( [ 'qci_type' => $this->getName() ] )
543 ->caller( $fname )->execute();
544 }
545
554 public function reallyDoQuery( $limit, $offset = false ) {
555 $fname = static::class . '::reallyDoQuery';
556 $dbr = $this->getRecacheDB();
557 $query = $this->getQueryInfo();
558 $order = $this->getOrderFields();
559
560 if ( $this->sortDescending() ) {
561 foreach ( $order as &$field ) {
562 $field .= ' DESC';
563 }
564 }
565
566 if ( is_array( $query ) ) {
567 $tables = isset( $query['tables'] ) ? (array)$query['tables'] : [];
568 $fields = isset( $query['fields'] ) ? (array)$query['fields'] : [];
569 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : [];
570 $options = isset( $query['options'] ) ? (array)$query['options'] : [];
571 $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : [];
572
573 $queryBuilder = $dbr->newSelectQueryBuilder()
574 ->tables( $tables )
575 ->fields( $fields )
576 ->conds( $conds )
577 ->caller( $fname )
578 ->options( $options )
579 ->joinConds( $join_conds );
580 if ( $order ) {
581 $queryBuilder->orderBy( $order );
582 }
583
584 if ( $limit !== false ) {
585 $queryBuilder->limit( intval( $limit ) );
586 }
587
588 if ( $offset !== false ) {
589 $queryBuilder->offset( intval( $offset ) );
590 }
591
592 $res = $queryBuilder->fetchResultSet();
593 } else {
594 // Old-fashioned raw SQL style, deprecated
595 MWDebug::detectDeprecatedOverride(
596 $this,
597 __CLASS__,
598 'getSQL',
599 '1.39'
600 );
601 $sql = $this->getSQL();
602 $sql .= ' ORDER BY ' . implode( ', ', $order );
603 $sql = $dbr->limitResult( $sql, $limit, $offset );
604 // phpcs:ignore MediaWiki.Usage.DbrQueryUsage.DbrQueryFound
605 $res = $dbr->query( $sql, $fname );
606 }
607
608 return $res;
609 }
610
617 public function doQuery( $offset = false, $limit = false ) {
618 if ( $this->isCached() && $this->isCacheable() ) {
619 return $this->fetchFromCache( $limit, $offset );
620 } else {
621 return $this->reallyDoQuery( $limit, $offset );
622 }
623 }
624
634 public function fetchFromCache( $limit, $offset = false ) {
635 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
636 $queryBuilder = $dbr->newSelectQueryBuilder()
637 ->select( [ 'qc_type', 'namespace' => 'qc_namespace', 'title' => 'qc_title', 'value' => 'qc_value' ] )
638 ->from( 'querycache' )
639 ->where( [ 'qc_type' => $this->getName() ] );
640
641 if ( $limit !== false ) {
642 $queryBuilder->limit( intval( $limit ) );
643 }
644
645 if ( $offset !== false ) {
646 $queryBuilder->offset( intval( $offset ) );
647 }
648
649 $order = $this->getCacheOrderFields();
650 if ( $this->sortDescending() ) {
651 $queryBuilder->orderBy( $order, SelectQueryBuilder::SORT_DESC );
652 } else {
653 $queryBuilder->orderBy( $order );
654 }
655
656 return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
657 }
658
666 protected function getCacheOrderFields() {
667 return [ 'value' ];
668 }
669
673 public function getCachedTimestamp() {
674 if ( $this->cachedTimestamp === null ) {
675 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
676 $fname = static::class . '::getCachedTimestamp';
677 $this->cachedTimestamp = $dbr->newSelectQueryBuilder()
678 ->select( 'qci_timestamp' )
679 ->from( 'querycache_info' )
680 ->where( [ 'qci_type' => $this->getName() ] )
681 ->caller( $fname )->fetchField();
682 }
683 return $this->cachedTimestamp;
684 }
685
698 protected function getLimitOffset() {
699 [ $limit, $offset ] = $this->getRequest()
700 ->getLimitOffsetForUser( $this->getUser() );
701 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
702 $maxResults = $this->getMaxResults();
703 // Can't display more than max results on a page
704 $limit = min( $limit, $maxResults );
705 // Can't skip over more than the end of $maxResults
706 $offset = min( $offset, $maxResults + 1 );
707 }
708 return [ $limit, $offset ];
709 }
710
719 protected function getDBLimit( $uiLimit, $uiOffset ) {
720 $maxResults = $this->getMaxResults();
721 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
722 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
723 return max( $limit, 0 );
724 } else {
725 return $uiLimit + 1;
726 }
727 }
728
739 protected function getMaxResults() {
740 // Max of 10000, unless we store more than 10000 in query cache.
741 return max( $this->getConfig()->get( MainConfigNames::QueryCacheLimit ), 10000 );
742 }
743
750 public function execute( $par ) {
751 $this->checkPermissions();
752
753 $this->setHeaders();
754 $this->outputHeader();
755
756 $out = $this->getOutput();
757
758 if ( $this->isCached() && !$this->isCacheable() ) {
759 $out->addWikiMsg( 'querypage-disabled' );
760 return;
761 }
762
763 $out->setSyndicated( $this->isSyndicated() );
764
765 if ( $this->limit == 0 && $this->offset == 0 ) {
766 [ $this->limit, $this->offset ] = $this->getLimitOffset();
767 }
768 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
769 // @todo Use doQuery()
770 if ( !$this->isCached() ) {
771 // select one extra row for navigation
772 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
773 } else {
774 // Get the cached result, select one extra row for navigation
775 $res = $this->fetchFromCache( $dbLimit, $this->offset );
776 if ( !$this->listoutput ) {
777 // Fetch the timestamp of this update
778 $ts = $this->getCachedTimestamp();
779 $lang = $this->getLanguage();
780 $maxResults = $lang->formatNum( $this->getConfig()->get(
782
783 if ( $ts ) {
784 $user = $this->getUser();
785 $updated = $lang->userTimeAndDate( $ts, $user );
786 $updateddate = $lang->userDate( $ts, $user );
787 $updatedtime = $lang->userTime( $ts, $user );
788 $out->addMeta( 'Data-Cache-Time', $ts );
789 $out->addJsConfigVars( 'dataCacheTime', $ts );
790 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
791 } else {
792 $out->addWikiMsg( 'perfcached', $maxResults );
793 }
794
795 // If updates on this page have been disabled, let the user know
796 // that the data set won't be refreshed for now
797 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
798 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
799 $runMode = $disabledQueryPages[$this->getName()];
800 if ( $runMode === 'disabled' ) {
801 $out->wrapWikiMsg(
802 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
803 'querypage-no-updates'
804 );
805 } else {
806 // Messages used here: querypage-updates-periodical
807 $out->wrapWikiMsg(
808 "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
809 'querypage-updates-' . $runMode
810 );
811 }
812 }
813 }
814 }
815
816 $this->numRows = $res->numRows();
817
818 $dbr = $this->getRecacheDB();
819 $this->preprocessResults( $dbr, $res );
820
821 $out->addHTML( Xml::openElement( 'div', [ 'class' => 'mw-spcontent' ] ) );
822
823 // Top header and navigation
824 if ( $this->shownavigation ) {
825 $out->addHTML( $this->getPageHeader() );
826 if ( $this->numRows > 0 ) {
827 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams(
828 min( $this->numRows, $this->limit ), // do not show the one extra row, if exist
829 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
830 // Disable the "next" link when we reach the end
831 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
832 && ( $this->offset + $this->limit >= $this->getMaxResults() );
833 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
834 $paging = $this->buildPrevNextNavigation( $this->offset,
835 $this->limit, $this->linkParameters(), $atEnd, $par );
836 $out->addHTML( '<p>' . $paging . '</p>' );
837 } else {
838 // No results to show, so don't bother with "showing X of Y" etc.
839 // -- just let the user know and give up now
840 $this->showEmptyText();
841 $out->addHTML( Xml::closeElement( 'div' ) );
842 return;
843 }
844 }
845
846 // The actual results; specialist subclasses will want to handle this
847 // with more than a straight list, so we hand them the info, plus
848 // an OutputPage, and let them get on with it
849 $this->outputResults( $out,
850 $this->getSkin(),
851 $dbr, // Should use IResultWrapper for this
852 $res,
853 min( $this->numRows, $this->limit ), // do not format the one extra row, if exist
854 $this->offset );
855
856 // Repeat the paging links at the bottom
857 if ( $this->shownavigation ) {
858 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here
859 $out->addHTML( '<p>' . $paging . '</p>' );
860 }
861
862 $out->addHTML( Xml::closeElement( 'div' ) );
863 }
864
878 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
879 if ( $num > 0 ) {
880 $html = [];
881 if ( !$this->listoutput ) {
882 $html[] = $this->openList( $offset );
883 }
884
885 // $res might contain the whole 1,000 rows, so we read up to
886 // $num [should update this to use a Pager]
887 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
888 $line = $this->formatResult( $skin, $row );
889 if ( $line ) {
890 $html[] = $this->listoutput
891 ? $line
892 : "<li>{$line}</li>\n";
893 }
894 }
895
896 if ( !$this->listoutput ) {
897 $html[] = $this->closeList();
898 }
899
900 $html = $this->listoutput
901 ? $this->getContentLanguage()->listToText( $html )
902 : implode( '', $html );
903
904 $out->addHTML( $html );
905 }
906 }
907
912 protected function openList( $offset ) {
913 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
914 }
915
919 protected function closeList() {
920 return "</ol>\n";
921 }
922
929 protected function preprocessResults( $db, $res ) {
930 }
931
944 protected function executeLBFromResultWrapper( IResultWrapper $res, $ns = null ) {
945 if ( !$res->numRows() ) {
946 return;
947 }
948
949 $batch = $this->getLinkBatchFactory()->newLinkBatch();
950 foreach ( $res as $row ) {
951 $batch->add( $ns ?? (int)$row->namespace, $row->title );
952 }
953 $batch->execute();
954
955 $res->seek( 0 );
956 }
957
963 final protected function setDBLoadBalancer( ILoadBalancer $loadBalancer ) {
964 $this->loadBalancer = $loadBalancer;
965 }
966
972 final protected function getDBLoadBalancer(): ILoadBalancer {
973 if ( $this->loadBalancer === null ) {
974 // Fallback if not provided
975 // TODO Change to wfWarn in a future release
976 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
977 }
978 return $this->loadBalancer;
979 }
980
985 final protected function setDatabaseProvider( IConnectionProvider $databaseProvider ) {
986 $this->databaseProvider = $databaseProvider;
987 }
988
993 final protected function getDatabaseProvider(): IConnectionProvider {
994 if ( $this->databaseProvider === null ) {
995 $this->databaseProvider = MediaWikiServices::getInstance()->getConnectionProvider();
996 }
997 return $this->databaseProvider;
998 }
999}
1000
1002class_alias( QueryPage::class, 'QueryPage' );
getUser()
getRequest()
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
Debug toolbar.
Definition MWDebug.php:48
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition Html.php:240
static closeElement( $element)
Returns "</$element>".
Definition Html.php:304
A class containing constants representing the names of configuration variables.
const DisableQueryPages
Name constant for the DisableQueryPages setting, for use with Config::get()
const QueryCacheLimit
Name constant for the QueryCacheLimit setting, for use with Config::get()
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
const DisableQueryPageUpdate
Name constant for the DisableQueryPageUpdate setting, for use with Config::get()
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.
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:89
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.
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:91
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:94
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
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.
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...
getSQL()
For back-compat, subclasses may return a raw SQL query here, as a string.
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.
Module of static functions for generating XML.
Definition Xml.php:37
The base class for all skins.
Definition Skin.php:59
Database error base class.
Definition DBError.php:36
Build SELECT queries with a fluent interface.
Interface for configuration instances.
Definition Config.php:32
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Represents the target of a wiki link.
Provide primary and replica IDatabase connections.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
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...