91 [ SpecialAncientPages::class,
'Ancientpages' ],
92 [ SpecialBrokenRedirects::class,
'BrokenRedirects' ],
93 [ SpecialDeadendPages::class,
'Deadendpages' ],
94 [ SpecialDoubleRedirects::class,
'DoubleRedirects' ],
95 [ SpecialListDuplicatedFiles::class,
'ListDuplicatedFiles' ],
96 [ SpecialLinkSearch::class,
'LinkSearch' ],
97 [ SpecialListRedirects::class,
'Listredirects' ],
98 [ SpecialLonelyPages::class,
'Lonelypages' ],
99 [ SpecialLongPages::class,
'Longpages' ],
100 [ SpecialMediaStatistics::class,
'MediaStatistics' ],
101 [ SpecialMIMESearch::class,
'MIMEsearch' ],
102 [ SpecialMostCategories::class,
'Mostcategories' ],
103 [ MostimagesPage::class,
'Mostimages' ],
104 [ SpecialMostInterwikis::class,
'Mostinterwikis' ],
105 [ SpecialMostLinkedCategories::class,
'Mostlinkedcategories' ],
106 [ SpecialMostLinkedTemplates::class,
'Mostlinkedtemplates' ],
107 [ SpecialMostLinked::class,
'Mostlinked' ],
108 [ SpecialMostRevisions::class,
'Mostrevisions' ],
109 [ SpecialFewestRevisions::class,
'Fewestrevisions' ],
110 [ SpecialShortPages::class,
'Shortpages' ],
111 [ SpecialUncategorizedCategories::class,
'Uncategorizedcategories' ],
112 [ SpecialUncategorizedPages::class,
'Uncategorizedpages' ],
113 [ SpecialUncategorizedImages::class,
'Uncategorizedimages' ],
114 [ SpecialUncategorizedTemplates::class,
'Uncategorizedtemplates' ],
115 [ SpecialUnusedCategories::class,
'Unusedcategories' ],
116 [ SpecialUnusedImages::class,
'Unusedimages' ],
117 [ SpecialWantedCategories::class,
'Wantedcategories' ],
118 [ WantedFilesPage::class,
'Wantedfiles' ],
119 [ WantedPagesPage::class,
'Wantedpages' ],
120 [ SpecialWantedTemplates::class,
'Wantedtemplates' ],
121 [ SpecialUnwatchedPages::class,
'Unwatchedpages' ],
122 [ SpecialUnusedTemplates::class,
'Unusedtemplates' ],
123 [ SpecialWithoutInterwiki::class,
'Withoutinterwiki' ],
125 Hooks::runner()->onWgQueryPages( $qp );
144 if ( $this->linkBatchFactory === null ) {
147 $this->linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
158 $disableQueryPageUpdate = $config->
get(
'DisableQueryPageUpdate' );
160 if ( !is_array( $disableQueryPageUpdate ) ) {
165 foreach ( $disableQueryPageUpdate as $name => $runMode ) {
166 if ( is_int( $name ) ) {
168 $pages[$runMode] =
'disabled';
170 $pages[$name] = $runMode;
182 $this->listoutput = $bool;
225 throw new MWException(
"Bug in a QueryPage: doesn't implement getQueryInfo() nor "
226 .
"getQuery() properly" );
275 return $this->getConfig()->get(
'DisableQueryPages' );
298 return $this->isExpensive() && $this->getConfig()->get(
'MiserMode' );
340 $this->getOutput()->addWikiMsg(
'specialpage-empty' );
365 public function recache( $limit, $ignoreErrors =
true ) {
366 if ( !$this->isCacheable() ) {
370 $fname = static::class .
'::recache';
371 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
375 $res = $this->reallyDoQuery( $limit,
false );
378 $num =
$res->numRows();
381 foreach (
$res as $i => $row ) {
382 if ( isset( $row->value ) ) {
383 if ( $this->usesTimestamps() ) {
387 $value = intval( $row->value );
394 'qc_type' => $this->getName(),
395 'qc_namespace' => $row->namespace,
396 'qc_title' => $row->title,
401 $dbw->doAtomicSection(
403 function (
IDatabase $dbw, $fname ) use ( $vals ) {
405 $dbw->
delete(
'querycache',
406 [
'qc_type' => $this->getName() ],
410 if ( count( $vals ) ) {
411 $dbw->
insert(
'querycache', $vals, $fname );
414 $dbw->
delete(
'querycache_info',
415 [
'qci_type' => $this->getName() ],
418 $dbw->
insert(
'querycache_info',
419 [
'qci_type' => $this->getName(),
427 if ( !$ignoreErrors ) {
442 return $this->getDBLoadBalancer()
443 ->getConnectionRef( ILoadBalancer::DB_REPLICA, [ $this->getName(),
'QueryPage::recache',
'vslow' ] );
453 if ( $this->isCached() ) {
454 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
455 $dbw->delete(
'querycache', [
456 'qc_type' => $this->getName(),
457 'qc_namespace' =>
$title->getNamespace(),
458 'qc_title' =>
$title->getDBkey(),
469 $fname = static::class .
'::' . __FUNCTION__;
470 $dbw = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_PRIMARY );
471 $dbw->delete(
'querycache',
472 [
'qc_type' => $this->getName() ],
475 $dbw->delete(
'querycachetwo',
476 [
'qcc_type' => $this->getName() ],
479 $dbw->delete(
'querycache_info',
480 [
'qci_type' => $this->getName() ],
494 $fname = static::class .
'::reallyDoQuery';
495 $dbr = $this->getRecacheDB();
496 $query = $this->getQueryInfo();
497 $order = $this->getOrderFields();
499 if ( $this->sortDescending() ) {
500 foreach ( $order as &$field ) {
505 if ( is_array( $query ) ) {
506 $tables = isset( $query[
'tables'] ) ? (array)$query[
'tables'] : [];
507 $fields = isset( $query[
'fields'] ) ? (array)$query[
'fields'] : [];
508 $conds = isset( $query[
'conds'] ) ? (array)$query[
'conds'] : [];
509 $options = isset( $query[
'options'] ) ? (array)$query[
'options'] : [];
510 $join_conds = isset( $query[
'join_conds'] ) ? (array)$query[
'join_conds'] : [];
513 $options[
'ORDER BY'] = $order;
516 if ( $limit !==
false ) {
517 $options[
'LIMIT'] = intval( $limit );
520 if ( $offset !==
false ) {
521 $options[
'OFFSET'] = intval( $offset );
524 $res =
$dbr->select( $tables, $fields, $conds, $fname,
525 $options, $join_conds
529 $sql = $this->getSQL();
530 $sql .=
' ORDER BY ' . implode(
', ', $order );
531 $sql =
$dbr->limitResult( $sql, $limit, $offset );
544 public function doQuery( $offset =
false, $limit =
false ) {
545 if ( $this->isCached() && $this->isCacheable() ) {
546 return $this->fetchFromCache( $limit, $offset );
548 return $this->reallyDoQuery( $limit, $offset );
562 $dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
565 if ( $limit !==
false ) {
566 $options[
'LIMIT'] = intval( $limit );
569 if ( $offset !==
false ) {
570 $options[
'OFFSET'] = intval( $offset );
573 $order = $this->getCacheOrderFields();
574 if ( $this->sortDescending() ) {
575 foreach ( $order as &$field ) {
580 $options[
'ORDER BY'] = $order;
583 return $dbr->select(
'querycache',
585 'namespace' =>
'qc_namespace',
586 'title' =>
'qc_title',
587 'value' =>
'qc_value' ],
588 [
'qc_type' => $this->getName() ],
609 if ( $this->cachedTimestamp ===
null ) {
610 $dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
611 $fname = static::class .
'::getCachedTimestamp';
612 $this->cachedTimestamp =
$dbr->selectField(
'querycache_info',
'qci_timestamp',
613 [
'qci_type' => $this->getName() ], $fname );
615 return $this->cachedTimestamp;
631 list( $limit, $offset ) = $this->getRequest()
632 ->getLimitOffsetForUser( $this->getUser() );
633 if ( $this->getConfig()->
get(
'MiserMode' ) ) {
634 $maxResults = $this->getMaxResults();
636 $limit = min( $limit, $maxResults );
638 $offset = min( $offset, $maxResults + 1 );
640 return [ $limit, $offset ];
652 $maxResults = $this->getMaxResults();
653 if ( $this->getConfig()->
get(
'MiserMode' ) ) {
654 $limit = min( $uiLimit + 1, $maxResults - $uiOffset );
655 return max( $limit, 0 );
673 return max( $this->getConfig()->
get(
'QueryCacheLimit' ), 10000 );
683 $this->checkPermissions();
686 $this->outputHeader();
688 $out = $this->getOutput();
690 if ( $this->isCached() && !$this->isCacheable() ) {
691 $out->addWikiMsg(
'querypage-disabled' );
695 $out->setSyndicated( $this->isSyndicated() );
697 if ( $this->limit == 0 && $this->offset == 0 ) {
698 list( $this->limit, $this->offset ) = $this->getLimitOffset();
700 $dbLimit = $this->getDBLimit( $this->limit, $this->offset );
702 if ( !$this->isCached() ) {
704 $res = $this->reallyDoQuery( $dbLimit, $this->offset );
707 $res = $this->fetchFromCache( $dbLimit, $this->offset );
708 if ( !$this->listoutput ) {
710 $ts = $this->getCachedTimestamp();
711 $lang = $this->getLanguage();
712 $maxResults =
$lang->formatNum( $this->getConfig()->
get(
'QueryCacheLimit' ) );
715 $user = $this->getUser();
716 $updated =
$lang->userTimeAndDate( $ts, $user );
717 $updateddate =
$lang->userDate( $ts, $user );
718 $updatedtime =
$lang->userTime( $ts, $user );
719 $out->addMeta(
'Data-Cache-Time', $ts );
720 $out->addJsConfigVars(
'dataCacheTime', $ts );
721 $out->addWikiMsg(
'perfcachedts', $updated, $updateddate, $updatedtime, $maxResults );
723 $out->addWikiMsg(
'perfcached', $maxResults );
728 $disabledQueryPages = self::getDisabledQueryPages( $this->getConfig() );
729 if ( isset( $disabledQueryPages[$this->getName()] ) ) {
730 $runMode = $disabledQueryPages[$this->getName()];
731 if ( $runMode ===
'disabled' ) {
733 "<div class=\"mw-querypage-no-updates\">\n$1\n</div>",
734 'querypage-no-updates'
739 "<div class=\"mw-querypage-updates-" . $runMode .
"\">\n$1\n</div>",
740 'querypage-updates-' . $runMode
747 $this->numRows =
$res->numRows();
749 $dbr = $this->getRecacheDB();
750 $this->preprocessResults(
$dbr,
$res );
752 $out->addHTML( Xml::openElement(
'div', [
'class' =>
'mw-spcontent' ] ) );
755 if ( $this->shownavigation ) {
756 $out->addHTML( $this->getPageHeader() );
757 if ( $this->numRows > 0 ) {
758 $out->addHTML( $this->msg(
'showingresultsinrange' )->numParams(
759 min( $this->numRows, $this->limit ),
760 $this->offset + 1, ( min( $this->numRows, $this->limit ) + $this->offset ) )->parseAsBlock() );
762 $miserMaxResults = $this->getConfig()->get(
'MiserMode' )
763 && ( $this->offset + $this->limit >= $this->getMaxResults() );
764 $atEnd = ( $this->numRows <= $this->limit ) || $miserMaxResults;
765 $paging = $this->buildPrevNextNavigation( $this->offset,
766 $this->limit, $this->linkParameters(), $atEnd, $par );
767 $out->addHTML(
'<p>' . $paging .
'</p>' );
771 $this->showEmptyText();
772 $out->addHTML( Xml::closeElement(
'div' ) );
780 $this->outputResults( $out,
784 min( $this->numRows, $this->limit ),
788 if ( $this->shownavigation ) {
789 $out->addHTML(
'<p>' . $paging .
'</p>' );
792 $out->addHTML( Xml::closeElement(
'div' ) );
811 if ( !$this->listoutput ) {
812 $html[] = $this->openList( $offset );
817 for ( $i = 0; $i < $num && $row =
$res->fetchObject(); $i++ ) {
818 $line = $this->formatResult( $skin, $row );
820 $html[] = $this->listoutput
822 :
"<li>{$line}</li>\n";
826 if ( !$this->listoutput ) {
827 $html[] = $this->closeList();
830 $html = $this->listoutput
831 ? $this->getContentLanguage()->listToText( $html )
832 : implode(
'', $html );
834 $out->addHTML( $html );
843 return "\n<ol start='" . ( $offset + 1 ) .
"' class='special'>\n";
875 if ( !
$res->numRows() ) {
879 $batch = $this->getLinkBatchFactory()->newLinkBatch();
880 foreach (
$res as $row ) {
881 $batch->add( $ns ?? $row->namespace, $row->title );
893 $this->loadBalancer = $loadBalancer;
901 if ( $this->loadBalancer === null ) {
904 $this->loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
906 return $this->loadBalancer;
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
isExpensive()
Is this query expensive (for some definition of expensive)? Then we don't let it run in miser mode.
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 query pages disabled and with it's run mode.
recache( $limit, $ignoreErrors=true)
Clear the cache and save new results.
setDBLoadBalancer(ILoadBalancer $loadBalancer)
LinkBatchFactory null $linkBatchFactory
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()
Sometime 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
ILoadBalancer null $loadBalancer
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.
Interface for configuration instances.
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
if(!isset( $args[0])) $lang