MediaWiki master
QueryPage.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\SpecialPage;
25
26use Exception;
27use LogicException;
69use Skin;
70use stdClass;
78use Xml;
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, $ignoreErrors = true ) {
429 if ( !$this->isCacheable() ) {
430 return 0;
431 }
432
433 $fname = static::class . '::recache';
434 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
435
436 try {
437 // Do query
438 $res = $this->reallyDoQuery( $limit, false );
439 $num = false;
440 if ( $res ) {
441 $num = $res->numRows();
442 // Fetch results
443 $vals = [];
444 foreach ( $res as $i => $row ) {
445 if ( isset( $row->value ) ) {
446 if ( $this->usesTimestamps() ) {
447 $value = (int)wfTimestamp( TS_UNIX, $row->value );
448 } else {
449 $value = intval( $row->value ); // T16414
450 }
451 } else {
452 $value = $i;
453 }
454
455 $vals[] = [
456 'qc_type' => $this->getName(),
457 'qc_namespace' => $row->namespace,
458 'qc_title' => $row->title,
459 'qc_value' => $value
460 ];
461 }
462
463 $dbw->doAtomicSection(
464 $fname,
465 function ( IDatabase $dbw, $fname ) use ( $vals ) {
466 // Clear out any old cached data
468 ->deleteFrom( 'querycache' )
469 ->where( [ 'qc_type' => $this->getName() ] )
470 ->caller( $fname )->execute();
471 // Update the querycache_info record for the page
473 ->insertInto( 'querycache_info' )
474 ->row( [ 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ] )
475 ->onDuplicateKeyUpdate()
476 ->uniqueIndexFields( [ 'qci_type' ] )
477 ->set( [ 'qci_timestamp' => $dbw->timestamp() ] )
478 ->caller( $fname )->execute();
479 }
480 );
481 // Save results into the querycache table on the primary DB
482 if ( count( $vals ) ) {
483 foreach ( array_chunk( $vals, 500 ) as $chunk ) {
485 ->insertInto( 'querycache' )
486 ->rows( $chunk )
487 ->caller( $fname )->execute();
488 }
489 }
490 }
491 } catch ( DBError $e ) {
492 if ( !$ignoreErrors ) {
493 throw $e; // report query error
494 }
495 $num = false; // set result to false to indicate error
496 }
497
498 return $num;
499 }
500
506 protected function getRecacheDB() {
507 return $this->getDBLoadBalancer()
508 ->getConnection( ILoadBalancer::DB_REPLICA, 'vslow' );
509 }
510
517 public function delete( LinkTarget $title ) {
518 if ( $this->isCached() ) {
519 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
520 $dbw->newDeleteQueryBuilder()
521 ->deleteFrom( 'querycache' )
522 ->where( [
523 'qc_type' => $this->getName(),
524 'qc_namespace' => $title->getNamespace(),
525 'qc_title' => $title->getDBkey(),
526 ] )
527 ->caller( __METHOD__ )->execute();
528 }
529 }
530
536 public function deleteAllCachedData() {
537 $fname = static::class . '::' . __FUNCTION__;
538 $dbw = $this->getDatabaseProvider()->getPrimaryDatabase();
539 $dbw->newDeleteQueryBuilder()
540 ->deleteFrom( 'querycache' )
541 ->where( [ 'qc_type' => $this->getName() ] )
542 ->caller( $fname )->execute();
543 $dbw->newDeleteQueryBuilder()
544 ->deleteFrom( 'querycachetwo' )
545 ->where( [ 'qcc_type' => $this->getName() ] )
546 ->caller( $fname )->execute();
547 $dbw->newDeleteQueryBuilder()
548 ->deleteFrom( 'querycache_info' )
549 ->where( [ 'qci_type' => $this->getName() ] )
550 ->caller( $fname )->execute();
551 }
552
561 public function reallyDoQuery( $limit, $offset = false ) {
562 $fname = static::class . '::reallyDoQuery';
563 $dbr = $this->getRecacheDB();
564 $query = $this->getQueryInfo();
565 $order = $this->getOrderFields();
566
567 if ( $this->sortDescending() ) {
568 foreach ( $order as &$field ) {
569 $field .= ' DESC';
570 }
571 }
572
573 if ( is_array( $query ) ) {
574 $tables = isset( $query['tables'] ) ? (array)$query['tables'] : [];
575 $fields = isset( $query['fields'] ) ? (array)$query['fields'] : [];
576 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : [];
577 $options = isset( $query['options'] ) ? (array)$query['options'] : [];
578 $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : [];
579
580 $queryBuilder = $dbr->newSelectQueryBuilder()
581 ->tables( $tables )
582 ->fields( $fields )
583 ->conds( $conds )
584 ->caller( $fname )
585 ->options( $options )
586 ->joinConds( $join_conds );
587 if ( $order ) {
588 $queryBuilder->orderBy( $order );
589 }
590
591 if ( $limit !== false ) {
592 $queryBuilder->limit( intval( $limit ) );
593 }
594
595 if ( $offset !== false ) {
596 $queryBuilder->offset( intval( $offset ) );
597 }
598
599 $res = $queryBuilder->fetchResultSet();
600 } else {
601 // Old-fashioned raw SQL style, deprecated
602 MWDebug::detectDeprecatedOverride(
603 $this,
604 __CLASS__,
605 'getSQL',
606 '1.39'
607 );
608 $sql = $this->getSQL();
609 $sql .= ' ORDER BY ' . implode( ', ', $order );
610 $sql = $dbr->limitResult( $sql, $limit, $offset );
611 // phpcs:ignore MediaWiki.Usage.DbrQueryUsage.DbrQueryFound
612 $res = $dbr->query( $sql, $fname );
613 }
614
615 return $res;
616 }
617
624 public function doQuery( $offset = false, $limit = false ) {
625 if ( $this->isCached() && $this->isCacheable() ) {
626 return $this->fetchFromCache( $limit, $offset );
627 } else {
628 return $this->reallyDoQuery( $limit, $offset );
629 }
630 }
631
641 public function fetchFromCache( $limit, $offset = false ) {
642 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
643 $queryBuilder = $dbr->newSelectQueryBuilder()
644 ->select( [ 'qc_type', 'namespace' => 'qc_namespace', 'title' => 'qc_title', 'value' => 'qc_value' ] )
645 ->from( 'querycache' )
646 ->where( [ 'qc_type' => $this->getName() ] );
647
648 if ( $limit !== false ) {
649 $queryBuilder->limit( intval( $limit ) );
650 }
651
652 if ( $offset !== false ) {
653 $queryBuilder->offset( intval( $offset ) );
654 }
655
656 $order = $this->getCacheOrderFields();
657 if ( $this->sortDescending() ) {
658 $queryBuilder->orderBy( $order, SelectQueryBuilder::SORT_DESC );
659 } else {
660 $queryBuilder->orderBy( $order );
661 }
662
663 return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
664 }
665
673 protected function getCacheOrderFields() {
674 return [ 'value' ];
675 }
676
680 public function getCachedTimestamp() {
681 if ( $this->cachedTimestamp === null ) {
682 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
683 $fname = static::class . '::getCachedTimestamp';
684 $this->cachedTimestamp = $dbr->newSelectQueryBuilder()
685 ->select( 'qci_timestamp' )
686 ->from( 'querycache_info' )
687 ->where( [ 'qci_type' => $this->getName() ] )
688 ->caller( $fname )->fetchField();
689 }
690 return $this->cachedTimestamp;
691 }
692
705 protected function getLimitOffset() {
706 [ $limit, $offset ] = $this->getRequest()
707 ->getLimitOffsetForUser( $this->getUser() );
708 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
709 $maxResults = $this->getMaxResults();
710 // Can't display more than max results on a page
711 $limit = min( $limit, $maxResults );
712 // Can't skip over more than the end of $maxResults
713 $offset = min( $offset, $maxResults + 1 );
714 }
715 return [ $limit, $offset ];
716 }
717
726 protected function getDBLimit( $uiLimit, $uiOffset ) {
727 $maxResults = $this->getMaxResults();
728 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
729 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
730 return max( $limit, 0 );
731 } else {
732 return $uiLimit + 1;
733 }
734 }
735
746 protected function getMaxResults() {
747 // Max of 10000, unless we store more than 10000 in query cache.
748 return max( $this->getConfig()->get( MainConfigNames::QueryCacheLimit ), 10000 );
749 }
750
757 public function execute( $par ) {
758 $this->checkPermissions();
759
760 $this->setHeaders();
761 $this->outputHeader();
762
763 $out = $this->getOutput();
764
765 if ( $this->isCached() && !$this->isCacheable() ) {
766 $out->addWikiMsg( 'querypage-disabled' );
767 return;
768 }
769
770 $out->setSyndicated( $this->isSyndicated() );
771
772 if ( $this->limit == 0 && $this->offset == 0 ) {
773 [ $this->limit, $this->offset ] = $this->getLimitOffset();
774 }
775 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
776 // @todo Use doQuery()
777 if ( !$this->isCached() ) {
778 // select one extra row for navigation
779 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
780 } else {
781 // Get the cached result, select one extra row for navigation
782 $res = $this->fetchFromCache( $dbLimit, $this->offset );
783 if ( !$this->listoutput ) {
784 // Fetch the timestamp of this update
785 $ts = $this->getCachedTimestamp();
786 $lang = $this->getLanguage();
787 $maxResults = $lang->formatNum( $this->getConfig()->get(
789
790 if ( $ts ) {
791 $user = $this->getUser();
792 $updated = $lang->userTimeAndDate( $ts, $user );
793 $updateddate = $lang->userDate( $ts, $user );
794 $updatedtime = $lang->userTime( $ts, $user );
795 $out->addMeta( 'Data-Cache-Time', $ts );
796 $out->addJsConfigVars( 'dataCacheTime', $ts );
797 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
798 } else {
799 $out->addWikiMsg( 'perfcached', $maxResults );
800 }
801
802 // If updates on this page have been disabled, let the user know
803 // that the data set won't be refreshed for now
804 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
805 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
806 $runMode = $disabledQueryPages[$this->getName()];
807 if ( $runMode === 'disabled' ) {
808 $out->wrapWikiMsg(
809 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
810 'querypage-no-updates'
811 );
812 } else {
813 // Messages used here: querypage-updates-periodical
814 $out->wrapWikiMsg(
815 "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
816 'querypage-updates-' . $runMode
817 );
818 }
819 }
820 }
821 }
822
823 $this->numRows = $res->numRows();
824
825 $dbr = $this->getRecacheDB();
826 $this->preprocessResults( $dbr, $res );
827
828 $out->addHTML( Xml::openElement( 'div', [ 'class' => 'mw-spcontent' ] ) );
829
830 // Top header and navigation
831 if ( $this->shownavigation ) {
832 $out->addHTML( $this->getPageHeader() );
833 if ( $this->numRows > 0 ) {
834 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams(
835 min( $this->numRows, $this->limit ), // do not show the one extra row, if exist
836 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
837 // Disable the "next" link when we reach the end
838 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
839 && ( $this->offset + $this->limit >= $this->getMaxResults() );
840 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
841 $paging = $this->buildPrevNextNavigation( $this->offset,
842 $this->limit, $this->linkParameters(), $atEnd, $par );
843 $out->addHTML( '<p>' . $paging . '</p>' );
844 } else {
845 // No results to show, so don't bother with "showing X of Y" etc.
846 // -- just let the user know and give up now
847 $this->showEmptyText();
848 $out->addHTML( Xml::closeElement( 'div' ) );
849 return;
850 }
851 }
852
853 // The actual results; specialist subclasses will want to handle this
854 // with more than a straight list, so we hand them the info, plus
855 // an OutputPage, and let them get on with it
856 $this->outputResults( $out,
857 $this->getSkin(),
858 $dbr, // Should use IResultWrapper for this
859 $res,
860 min( $this->numRows, $this->limit ), // do not format the one extra row, if exist
861 $this->offset );
862
863 // Repeat the paging links at the bottom
864 if ( $this->shownavigation ) {
865 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here
866 $out->addHTML( '<p>' . $paging . '</p>' );
867 }
868
869 $out->addHTML( Xml::closeElement( 'div' ) );
870 }
871
885 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
886 if ( $num > 0 ) {
887 $html = [];
888 if ( !$this->listoutput ) {
889 $html[] = $this->openList( $offset );
890 }
891
892 // $res might contain the whole 1,000 rows, so we read up to
893 // $num [should update this to use a Pager]
894 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
895 $line = $this->formatResult( $skin, $row );
896 if ( $line ) {
897 $html[] = $this->listoutput
898 ? $line
899 : "<li>{$line}</li>\n";
900 }
901 }
902
903 if ( !$this->listoutput ) {
904 $html[] = $this->closeList();
905 }
906
907 $html = $this->listoutput
908 ? $this->getContentLanguage()->listToText( $html )
909 : implode( '', $html );
910
911 $out->addHTML( $html );
912 }
913 }
914
919 protected function openList( $offset ) {
920 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
921 }
922
926 protected function closeList() {
927 return "</ol>\n";
928 }
929
936 protected function preprocessResults( $db, $res ) {
937 }
938
951 protected function executeLBFromResultWrapper( IResultWrapper $res, $ns = null ) {
952 if ( !$res->numRows() ) {
953 return;
954 }
955
956 $batch = $this->getLinkBatchFactory()->newLinkBatch();
957 foreach ( $res as $row ) {
958 $batch->add( $ns ?? (int)$row->namespace, $row->title );
959 }
960 $batch->execute();
961
962 $res->seek( 0 );
963 }
964
969 final protected function setDBLoadBalancer( ILoadBalancer $loadBalancer ) {
970 $this->loadBalancer = $loadBalancer;
971 }
972
977 final protected function getDBLoadBalancer(): ILoadBalancer {
978 if ( $this->loadBalancer === null ) {
979 // Fallback if not provided
980 // TODO Change to wfWarn in a future release
981 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
982 }
983 return $this->loadBalancer;
984 }
985
990 final protected function setDatabaseProvider( IConnectionProvider $databaseProvider ) {
991 $this->databaseProvider = $databaseProvider;
992 }
993
998 final protected function getDatabaseProvider(): IConnectionProvider {
999 if ( $this->databaseProvider === null ) {
1000 $this->databaseProvider = MediaWikiServices::getInstance()->getConnectionProvider();
1001 }
1002 return $this->databaseProvider;
1003 }
1004}
1005
1007class_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...
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...
recache( $limit, $ignoreErrors=true)
Clear the cache and save new results.
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.
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.
A special page listing redirects to non existent page.
A special page that list pages that contain no link to other pages.
A special page listing redirects to redirecting page.
Special page for listing the articles with the fewest revisions.
Special:LinkSearch to search the external-links table.
Special:ListDuplicatedFiles Lists all files where the current version is a duplicate of the current v...
Special:Listredirects - Lists all the redirects on the wiki.
A special page looking for articles with no article linking to them, thus being lonely.
Searches the database for files of the requested MIME type, comparing this with the 'img_major_mime' ...
A special page that list pages that have highest category count.
A special page that lists most used images.
A special page that listed pages that have highest interwiki count.
A querypage to show categories ordered in descending order by the pages in them.
Special page lists templates with a large number of transclusion links, i.e.
A special page to show pages ordered by the number of pages linking to them.
SpecialShortpages extends QueryPage.
A special page that lists uncategorized categories.
Special page lists images which haven't been categorised.
A special page looking for page without any category.
Special page lists all uncategorised pages in the template namespace.
A special page that lists unused images.
A special page that lists unused templates.
A special page that displays a list of pages that are not on anyone's watchlist.
A querypage to list the most wanted categories - implements Special:Wantedcategories.
Querypage that lists the most wanted files.
A special page that lists most linked pages that does not exist.
A querypage to list the most wanted templates.
Special page lists pages without language links.
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.
Module of static functions for generating XML.
Definition Xml.php:33
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:36
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...