MediaWiki master
QueryPage.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\SpecialPage;
25
26use Exception;
27use LogicException;
68use MWDebug;
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 ->getConnectionRef( 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 if ( $order ) {
581 $options['ORDER BY'] = $order;
582 }
583
584 if ( $limit !== false ) {
585 $options['LIMIT'] = intval( $limit );
586 }
587
588 if ( $offset !== false ) {
589 $options['OFFSET'] = intval( $offset );
590 }
591
592 $res = $dbr->select( $tables, $fields, $conds, $fname,
593 $options, $join_conds
594 );
595 } else {
596 // Old-fashioned raw SQL style, deprecated
597 MWDebug::detectDeprecatedOverride(
598 $this,
599 __CLASS__,
600 'getSQL',
601 '1.39'
602 );
603 $sql = $this->getSQL();
604 $sql .= ' ORDER BY ' . implode( ', ', $order );
605 $sql = $dbr->limitResult( $sql, $limit, $offset );
606 // phpcs:ignore MediaWiki.Usage.DbrQueryUsage.DbrQueryFound
607 $res = $dbr->query( $sql, $fname );
608 }
609
610 return $res;
611 }
612
619 public function doQuery( $offset = false, $limit = false ) {
620 if ( $this->isCached() && $this->isCacheable() ) {
621 return $this->fetchFromCache( $limit, $offset );
622 } else {
623 return $this->reallyDoQuery( $limit, $offset );
624 }
625 }
626
636 public function fetchFromCache( $limit, $offset = false ) {
637 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
638 $queryBuilder = $dbr->newSelectQueryBuilder()
639 ->select( [ 'qc_type', 'namespace' => 'qc_namespace', 'title' => 'qc_title', 'value' => 'qc_value' ] )
640 ->from( 'querycache' )
641 ->where( [ 'qc_type' => $this->getName() ] );
642
643 if ( $limit !== false ) {
644 $queryBuilder->limit( intval( $limit ) );
645 }
646
647 if ( $offset !== false ) {
648 $queryBuilder->offset( intval( $offset ) );
649 }
650
651 $order = $this->getCacheOrderFields();
652 if ( $this->sortDescending() ) {
653 $queryBuilder->orderBy( $order, SelectQueryBuilder::SORT_DESC );
654 } else {
655 $queryBuilder->orderBy( $order );
656 }
657
658 return $queryBuilder->caller( __METHOD__ )->fetchResultSet();
659 }
660
668 protected function getCacheOrderFields() {
669 return [ 'value' ];
670 }
671
675 public function getCachedTimestamp() {
676 if ( $this->cachedTimestamp === null ) {
677 $dbr = $this->getDatabaseProvider()->getReplicaDatabase();
678 $fname = static::class . '::getCachedTimestamp';
679 $this->cachedTimestamp = $dbr->newSelectQueryBuilder()
680 ->select( 'qci_timestamp' )
681 ->from( 'querycache_info' )
682 ->where( [ 'qci_type' => $this->getName() ] )
683 ->caller( $fname )->fetchField();
684 }
685 return $this->cachedTimestamp;
686 }
687
700 protected function getLimitOffset() {
701 [ $limit, $offset ] = $this->getRequest()
702 ->getLimitOffsetForUser( $this->getUser() );
703 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
704 $maxResults = $this->getMaxResults();
705 // Can't display more than max results on a page
706 $limit = min( $limit, $maxResults );
707 // Can't skip over more than the end of $maxResults
708 $offset = min( $offset, $maxResults + 1 );
709 }
710 return [ $limit, $offset ];
711 }
712
721 protected function getDBLimit( $uiLimit, $uiOffset ) {
722 $maxResults = $this->getMaxResults();
723 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
724 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
725 return max( $limit, 0 );
726 } else {
727 return $uiLimit + 1;
728 }
729 }
730
741 protected function getMaxResults() {
742 // Max of 10000, unless we store more than 10000 in query cache.
743 return max( $this->getConfig()->get( MainConfigNames::QueryCacheLimit ), 10000 );
744 }
745
752 public function execute( $par ) {
753 $this->checkPermissions();
754
755 $this->setHeaders();
756 $this->outputHeader();
757
758 $out = $this->getOutput();
759
760 if ( $this->isCached() && !$this->isCacheable() ) {
761 $out->addWikiMsg( 'querypage-disabled' );
762 return;
763 }
764
765 $out->setSyndicated( $this->isSyndicated() );
766
767 if ( $this->limit == 0 && $this->offset == 0 ) {
768 [ $this->limit, $this->offset ] = $this->getLimitOffset();
769 }
770 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
771 // @todo Use doQuery()
772 if ( !$this->isCached() ) {
773 // select one extra row for navigation
774 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
775 } else {
776 // Get the cached result, select one extra row for navigation
777 $res = $this->fetchFromCache( $dbLimit, $this->offset );
778 if ( !$this->listoutput ) {
779 // Fetch the timestamp of this update
780 $ts = $this->getCachedTimestamp();
781 $lang = $this->getLanguage();
782 $maxResults = $lang->formatNum( $this->getConfig()->get(
784
785 if ( $ts ) {
786 $user = $this->getUser();
787 $updated = $lang->userTimeAndDate( $ts, $user );
788 $updateddate = $lang->userDate( $ts, $user );
789 $updatedtime = $lang->userTime( $ts, $user );
790 $out->addMeta( 'Data-Cache-Time', $ts );
791 $out->addJsConfigVars( 'dataCacheTime', $ts );
792 $out->addWikiMsg( 'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
793 } else {
794 $out->addWikiMsg( 'perfcached', $maxResults );
795 }
796
797 // If updates on this page have been disabled, let the user know
798 // that the data set won't be refreshed for now
799 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
800 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
801 $runMode = $disabledQueryPages[$this->getName()];
802 if ( $runMode === 'disabled' ) {
803 $out->wrapWikiMsg(
804 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
805 'querypage-no-updates'
806 );
807 } else {
808 // Messages used here: querypage-updates-periodical
809 $out->wrapWikiMsg(
810 "<div class=\"mw-querypage-updates-" . $runMode . "\">\n$1\n</div>",
811 'querypage-updates-' . $runMode
812 );
813 }
814 }
815 }
816 }
817
818 $this->numRows = $res->numRows();
819
820 $dbr = $this->getRecacheDB();
821 $this->preprocessResults( $dbr, $res );
822
823 $out->addHTML( Xml::openElement( 'div', [ 'class' => 'mw-spcontent' ] ) );
824
825 // Top header and navigation
826 if ( $this->shownavigation ) {
827 $out->addHTML( $this->getPageHeader() );
828 if ( $this->numRows > 0 ) {
829 $out->addHTML( $this->msg( 'showingresultsinrange' )->numParams(
830 min( $this->numRows, $this->limit ), // do not show the one extra row, if exist
831 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
832 // Disable the "next" link when we reach the end
833 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
834 && ( $this->offset + $this->limit >= $this->getMaxResults() );
835 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
836 $paging = $this->buildPrevNextNavigation( $this->offset,
837 $this->limit, $this->linkParameters(), $atEnd, $par );
838 $out->addHTML( '<p>' . $paging . '</p>' );
839 } else {
840 // No results to show, so don't bother with "showing X of Y" etc.
841 // -- just let the user know and give up now
842 $this->showEmptyText();
843 $out->addHTML( Xml::closeElement( 'div' ) );
844 return;
845 }
846 }
847
848 // The actual results; specialist subclasses will want to handle this
849 // with more than a straight list, so we hand them the info, plus
850 // an OutputPage, and let them get on with it
851 $this->outputResults( $out,
852 $this->getSkin(),
853 $dbr, // Should use IResultWrapper for this
854 $res,
855 min( $this->numRows, $this->limit ), // do not format the one extra row, if exist
856 $this->offset );
857
858 // Repeat the paging links at the bottom
859 if ( $this->shownavigation ) {
860 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable paging is set when used here
861 $out->addHTML( '<p>' . $paging . '</p>' );
862 }
863
864 $out->addHTML( Xml::closeElement( 'div' ) );
865 }
866
880 protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
881 if ( $num > 0 ) {
882 $html = [];
883 if ( !$this->listoutput ) {
884 $html[] = $this->openList( $offset );
885 }
886
887 // $res might contain the whole 1,000 rows, so we read up to
888 // $num [should update this to use a Pager]
889 for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
890 $line = $this->formatResult( $skin, $row );
891 if ( $line ) {
892 $html[] = $this->listoutput
893 ? $line
894 : "<li>{$line}</li>\n";
895 }
896 }
897
898 if ( !$this->listoutput ) {
899 $html[] = $this->closeList();
900 }
901
902 $html = $this->listoutput
903 ? $this->getContentLanguage()->listToText( $html )
904 : implode( '', $html );
905
906 $out->addHTML( $html );
907 }
908 }
909
914 protected function openList( $offset ) {
915 return "\n<ol start='" . ( $offset + 1 ) . "' class='special'>\n";
916 }
917
921 protected function closeList() {
922 return "</ol>\n";
923 }
924
931 protected function preprocessResults( $db, $res ) {
932 }
933
946 protected function executeLBFromResultWrapper( IResultWrapper $res, $ns = null ) {
947 if ( !$res->numRows() ) {
948 return;
949 }
950
951 $batch = $this->getLinkBatchFactory()->newLinkBatch();
952 foreach ( $res as $row ) {
953 $batch->add( $ns ?? (int)$row->namespace, $row->title );
954 }
955 $batch->execute();
956
957 $res->seek( 0 );
958 }
959
964 final protected function setDBLoadBalancer( ILoadBalancer $loadBalancer ) {
965 $this->loadBalancer = $loadBalancer;
966 }
967
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:42
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:58
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...