76 private $loadBalancer =
null;
79 private $linkBatchFactory =
null;
96 [ SpecialAncientPages::class,
'Ancientpages' ],
97 [ SpecialBrokenRedirects::class,
'BrokenRedirects' ],
98 [ SpecialDeadendPages::class,
'Deadendpages' ],
99 [ SpecialDoubleRedirects::class,
'DoubleRedirects' ],
100 [ SpecialListDuplicatedFiles::class,
'ListDuplicatedFiles' ],
101 [ SpecialLinkSearch::class,
'LinkSearch' ],
102 [ SpecialListRedirects::class,
'Listredirects' ],
103 [ SpecialLonelyPages::class,
'Lonelypages' ],
104 [ SpecialLongPages::class,
'Longpages' ],
106 [ SpecialMIMESearch::class,
'MIMEsearch' ],
107 [ SpecialMostCategories::class,
'Mostcategories' ],
108 [ SpecialMostImages::class,
'Mostimages' ],
109 [ SpecialMostInterwikis::class,
'Mostinterwikis' ],
110 [ SpecialMostLinkedCategories::class,
'Mostlinkedcategories' ],
111 [ SpecialMostLinkedTemplates::class,
'Mostlinkedtemplates' ],
112 [ SpecialMostLinked::class,
'Mostlinked' ],
113 [ SpecialMostRevisions::class,
'Mostrevisions' ],
114 [ SpecialFewestRevisions::class,
'Fewestrevisions' ],
115 [ SpecialShortPages::class,
'Shortpages' ],
116 [ SpecialUncategorizedCategories::class,
'Uncategorizedcategories' ],
117 [ SpecialUncategorizedPages::class,
'Uncategorizedpages' ],
118 [ SpecialUncategorizedImages::class,
'Uncategorizedimages' ],
119 [ SpecialUncategorizedTemplates::class,
'Uncategorizedtemplates' ],
120 [ SpecialUnusedCategories::class,
'Unusedcategories' ],
121 [ SpecialUnusedImages::class,
'Unusedimages' ],
122 [ SpecialWantedCategories::class,
'Wantedcategories' ],
123 [ SpecialWantedFiles::class,
'Wantedfiles' ],
124 [ SpecialWantedPages::class,
'Wantedpages' ],
125 [ SpecialWantedTemplates::class,
'Wantedtemplates' ],
126 [ SpecialUnwatchedPages::class,
'Unwatchedpages' ],
127 [ SpecialUnusedTemplates::class,
'Unusedtemplates' ],
128 [ SpecialWithoutInterwiki::class,
'Withoutinterwiki' ],
141 $this->linkBatchFactory = $linkBatchFactory;
149 if ( $this->linkBatchFactory === null ) {
152 $this->linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
154 return $this->linkBatchFactory;
163 $disableQueryPageUpdate = $config->
get( MainConfigNames::DisableQueryPageUpdate );
165 if ( !is_array( $disableQueryPageUpdate ) ) {
170 foreach ( $disableQueryPageUpdate as $name => $runMode ) {
171 if ( is_int( $name ) ) {
173 $pages[$runMode] =
'disabled';
175 $pages[$name] = $runMode;
187 $this->listoutput = $bool;
231 throw new MWException(
"Bug in a QueryPage: doesn't implement getQueryInfo() nor "
232 .
"getQuery() properly" );
293 return $this->getConfig()->get( MainConfigNames::DisableQueryPages );
316 return $this->isExpensive() && $this->getConfig()->get( MainConfigNames::MiserMode );
358 $this->getOutput()->addWikiMsg(
'specialpage-empty' );
383 public function recache( $limit, $ignoreErrors =
true ) {
384 if ( !$this->isCacheable() ) {
388 $fname = static::class .
'::recache';
393 $res = $this->reallyDoQuery( $limit,
false );
396 $num =
$res->numRows();
399 foreach (
$res as $i => $row ) {
400 if ( isset( $row->value ) ) {
401 if ( $this->usesTimestamps() ) {
404 $value = intval( $row->value );
411 'qc_type' => $this->getName(),
412 'qc_namespace' => $row->namespace,
413 'qc_title' => $row->title,
418 $dbw->doAtomicSection(
420 function (
IDatabase $dbw, $fname ) use ( $vals ) {
422 $dbw->
delete(
'querycache',
423 [
'qc_type' => $this->getName() ],
427 if ( count( $vals ) ) {
428 $dbw->
insert(
'querycache', $vals, $fname );
434 'qci_type' => $this->getName(),
447 if ( !$ignoreErrors ) {
462 return $this->getDBLoadBalancer()
473 if ( $this->isCached() ) {
475 $dbw->delete(
'querycache', [
476 'qc_type' => $this->getName(),
477 'qc_namespace' =>
$title->getNamespace(),
478 'qc_title' =>
$title->getDBkey(),
489 $fname = static::class .
'::' . __FUNCTION__;
491 $dbw->delete(
'querycache',
492 [
'qc_type' => $this->getName() ],
495 $dbw->delete(
'querycachetwo',
496 [
'qcc_type' => $this->getName() ],
499 $dbw->delete(
'querycache_info',
500 [
'qci_type' => $this->getName() ],
514 $fname = static::class .
'::reallyDoQuery';
515 $dbr = $this->getRecacheDB();
516 $query = $this->getQueryInfo();
517 $order = $this->getOrderFields();
519 if ( $this->sortDescending() ) {
520 foreach ( $order as &$field ) {
525 if ( is_array( $query ) ) {
526 $tables = isset( $query[
'tables'] ) ? (array)$query[
'tables'] : [];
527 $fields = isset( $query[
'fields'] ) ? (array)$query[
'fields'] : [];
528 $conds = isset( $query[
'conds'] ) ? (array)$query[
'conds'] : [];
529 $options = isset( $query[
'options'] ) ? (array)$query[
'options'] : [];
530 $join_conds = isset( $query[
'join_conds'] ) ? (array)$query[
'join_conds'] : [];
533 $options[
'ORDER BY'] = $order;
536 if ( $limit !==
false ) {
537 $options[
'LIMIT'] = intval( $limit );
540 if ( $offset !==
false ) {
541 $options[
'OFFSET'] = intval( $offset );
544 $res =
$dbr->select( $tables, $fields, $conds, $fname,
545 $options, $join_conds
555 $sql = $this->getSQL();
556 $sql .=
' ORDER BY ' . implode(
', ', $order );
557 $sql =
$dbr->limitResult( $sql, $limit, $offset );
570 public function doQuery( $offset =
false, $limit =
false ) {
571 if ( $this->isCached() && $this->isCacheable() ) {
572 return $this->fetchFromCache( $limit, $offset );
574 return $this->reallyDoQuery( $limit, $offset );
591 if ( $limit !==
false ) {
592 $options[
'LIMIT'] = intval( $limit );
595 if ( $offset !==
false ) {
596 $options[
'OFFSET'] = intval( $offset );
599 $order = $this->getCacheOrderFields();
600 if ( $this->sortDescending() ) {
601 foreach ( $order as &$field ) {
606 $options[
'ORDER BY'] = $order;
609 return $dbr->select(
'querycache',
611 'namespace' =>
'qc_namespace',
612 'title' =>
'qc_title',
613 'value' =>
'qc_value' ],
614 [
'qc_type' => $this->getName() ],
635 if ( $this->cachedTimestamp ===
null ) {
637 $fname = static::class .
'::getCachedTimestamp';
638 $this->cachedTimestamp =
$dbr->selectField(
'querycache_info',
'qci_timestamp',
639 [
'qci_type' => $this->getName() ], $fname );
641 return $this->cachedTimestamp;
658 ->getLimitOffsetForUser( $this->
getUser() );
659 if ( $this->getConfig()->
get( MainConfigNames::MiserMode ) ) {
660 $maxResults = $this->getMaxResults();
662 $limit = min( $limit, $maxResults );
664 $offset = min( $offset, $maxResults + 1 );
666 return [ $limit, $offset ];
678 $maxResults = $this->getMaxResults();
679 if ( $this->getConfig()->
get( MainConfigNames::MiserMode ) ) {
680 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
681 return max( $limit, 0 );
699 return max( $this->getConfig()->
get( MainConfigNames::QueryCacheLimit ), 10000 );
709 $this->checkPermissions();
712 $this->outputHeader();
714 $out = $this->getOutput();
716 if ( $this->isCached() && !$this->isCacheable() ) {
717 $out->addWikiMsg(
'querypage-disabled' );
721 $out->setSyndicated( $this->isSyndicated() );
723 if ( $this->limit == 0 && $this->offset == 0 ) {
724 [ $this->limit, $this->offset ] = $this->getLimitOffset();
726 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
728 if ( !$this->isCached() ) {
730 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
733 $res = $this->fetchFromCache( $dbLimit, $this->offset );
734 if ( !$this->listoutput ) {
736 $ts = $this->getCachedTimestamp();
737 $lang = $this->getLanguage();
738 $maxResults =
$lang->formatNum( $this->getConfig()->
get(
739 MainConfigNames::QueryCacheLimit ) );
743 $updated =
$lang->userTimeAndDate( $ts, $user );
744 $updateddate =
$lang->userDate( $ts, $user );
745 $updatedtime =
$lang->userTime( $ts, $user );
746 $out->addMeta(
'Data-Cache-Time', $ts );
747 $out->addJsConfigVars(
'dataCacheTime', $ts );
748 $out->addWikiMsg(
'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
750 $out->addWikiMsg(
'perfcached', $maxResults );
755 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
756 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
757 $runMode = $disabledQueryPages[$this->getName()];
758 if ( $runMode ===
'disabled' ) {
760 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
761 'querypage-no-updates'
766 "<div class=\"mw-querypage-updates-" . $runMode .
"\">\n$1\n</div>",
767 'querypage-updates-' . $runMode
774 $this->numRows =
$res->numRows();
776 $dbr = $this->getRecacheDB();
777 $this->preprocessResults(
$dbr,
$res );
782 if ( $this->shownavigation ) {
783 $out->addHTML( $this->getPageHeader() );
784 if ( $this->numRows > 0 ) {
785 $out->addHTML( $this->msg(
'showingresultsinrange' )->numParams(
786 min( $this->numRows, $this->limit ),
787 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
789 $miserMaxResults = $this->getConfig()->get( MainConfigNames::MiserMode )
790 && ( $this->offset + $this->limit >= $this->getMaxResults() );
791 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
792 $paging = $this->buildPrevNextNavigation( $this->offset,
793 $this->limit, $this->linkParameters(), $atEnd, $par );
794 $out->addHTML(
'<p>' . $paging .
'</p>' );
798 $this->showEmptyText();
807 $this->outputResults( $out,
811 min( $this->numRows, $this->limit ),
815 if ( $this->shownavigation ) {
817 $out->addHTML(
'<p>' . $paging .
'</p>' );
839 if ( !$this->listoutput ) {
840 $html[] = $this->openList( $offset );
845 for ( $i = 0; $i < $num && $row =
$res->fetchObject(); $i++ ) {
846 $line = $this->formatResult( $skin, $row );
848 $html[] = $this->listoutput
850 :
"<li>{$line}</li>\n";
854 if ( !$this->listoutput ) {
855 $html[] = $this->closeList();
858 $html = $this->listoutput
859 ? $this->getContentLanguage()->listToText( $html )
860 : implode(
'', $html );
862 $out->addHTML( $html );
871 return "\n<ol start='" . ( $offset + 1 ) .
"' class='special'>\n";
903 if ( !
$res->numRows() ) {
907 $batch = $this->getLinkBatchFactory()->newLinkBatch();
908 foreach (
$res as $row ) {
909 $batch->add( $ns ?? (
int)$row->namespace, $row->title );
921 $this->loadBalancer = $loadBalancer;
929 if ( $this->loadBalancer === null ) {
932 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
934 return $this->loadBalancer;
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'))
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
static detectDeprecatedOverride( $instance, $class, $method, $version=false, $component=false, $callerOffset=2)
Show a warning if $method declared in $class is overridden in $instance.
A class containing constants representing the names of configuration variables.
Implements Special:Ancientpages.
A special page that lists most linked pages that does not exist.
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
isExpensive()
Should this query page only be updated offline on large wikis?
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array.
getMaxResults()
Get max number of results we can return in miser mode.
doQuery( $offset=false, $limit=false)
Somewhat deprecated, you probably want to be using execute()
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
setListoutput( $bool)
A mutator for $this->listoutput;.
static getDisabledQueryPages(Config $config)
Get a list of disabled query pages and their run mode.
recache( $limit, $ignoreErrors=true)
Clear the cache and save new results.
setDBLoadBalancer(ILoadBalancer $loadBalancer)
fetchFromCache( $limit, $offset=false)
Fetch the query results from the query cache.
int $offset
The offset and limit in use, as passed to the query() function.
outputResults( $out, $skin, $dbr, $res, $num, $offset)
Format and output report results using the given information plus OutputPage.
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
sortDescending()
Override to sort by increasing values.
deleteAllCachedData()
Remove all cached value This is needed when the page is no longer using the cache.
formatResult( $skin, $result)
Formats the results of the query for display.
isSyndicated()
Sometimes we don't want to build rss / atom feeds.
static getPages()
Get a list of query page classes and their associated special pages, for periodic updates.
bool $shownavigation
Whether to show prev/next links.
reallyDoQuery( $limit, $offset=false)
Run the query and return the result.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
getOrderFields()
Subclasses return an array of fields to order by here.
showEmptyText()
Outputs some kind of an informative message (via OutputPage) to let the user know that the query retu...
usesTimestamps()
Does this query return timestamps rather than integers in its 'value' field? If true,...
getCacheOrderFields()
Return the order fields for fetchFromCache.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
getDBLimit( $uiLimit, $uiOffset)
What is limit to fetch from DB.
int $numRows
The number of rows returned by the query.
preprocessResults( $db, $res)
Do any necessary preprocessing of the result object.
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
getSQL()
For back-compat, subclasses may return a raw SQL query here, as a string.
getPageHeader()
The content returned by this function will be output before any result.
bool $listoutput
Whether or not we want plain listoutput rather than an ordered list.
execute( $par)
This is the actual workhorse.
string null false $cachedTimestamp
getLimitOffset()
Returns limit and offset, as returned by $this->getRequest()->getLimitOffsetForUser().
getRecacheDB()
Get a DB connection to be used for slow recache queries.
Parent class for all special pages.
static closeElement( $element)
Shortcut to close an XML element.
static openElement( $element, $attribs=null)
This opens an XML element.
Interface for configuration instances.
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
if(!isset( $args[0])) $lang