MediaWiki master
SpecialShortPages.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
30use Skin;
31use stdClass;
35
42
43 private NamespaceInfo $namespaceInfo;
44
50 public function __construct(
51 NamespaceInfo $namespaceInfo,
52 IConnectionProvider $dbProvider,
53 LinkBatchFactory $linkBatchFactory
54 ) {
55 parent::__construct( 'Shortpages' );
56 $this->namespaceInfo = $namespaceInfo;
57 $this->setDatabaseProvider( $dbProvider );
58 $this->setLinkBatchFactory( $linkBatchFactory );
59 }
60
61 public function isSyndicated() {
62 return false;
63 }
64
65 public function getQueryInfo() {
66 $config = $this->getConfig();
67 $tables = [ 'page' ];
68 $conds = [
69 'page_namespace' => array_diff(
70 $this->namespaceInfo->getContentNamespaces(),
72 ),
73 'page_is_redirect' => 0
74 ];
75 $joinConds = [];
76 $options = [ 'USE INDEX' => [ 'page' => 'page_redirect_namespace_len' ] ];
77
78 // Allow extensions to modify the query
79 $this->getHookRunner()->onShortPagesQuery( $tables, $conds, $joinConds, $options );
80
81 return [
82 'tables' => $tables,
83 'fields' => [
84 'namespace' => 'page_namespace',
85 'title' => 'page_title',
86 'value' => 'page_len'
87 ],
88 'conds' => $conds,
89 'join_conds' => $joinConds,
90 'options' => $options
91 ];
92 }
93
94 public function reallyDoQuery( $limit, $offset = false ) {
95 $fname = static::class . '::reallyDoQuery';
96 $dbr = $this->getRecacheDB();
97 $query = $this->getQueryInfo();
98 $conds = isset( $query['conds'] ) ? (array)$query['conds'] : [];
99 $namespaces = $conds['page_namespace'];
100 unset( $conds['page_namespace'] );
101
102 if ( count( $namespaces ) === 1 || !$dbr->unionSupportsOrderAndLimit() ) {
103 return parent::reallyDoQuery( $limit, $offset );
104 }
105
106 // Optimization: Fix slow query on MySQL the case of multiple content namespaces,
107 // by rewriting this as a UNION of separate single namespace queries (T168010).
108 $sqb = $dbr->newSelectQueryBuilder()
109 ->select( isset( $query['fields'] ) ? (array)$query['fields'] : [] )
110 ->tables( isset( $query['tables'] ) ? (array)$query['tables'] : [] )
111 ->where( $conds )
112 ->options( isset( $query['options'] ) ? (array)$query['options'] : [] )
113 ->joinConds( isset( $query['join_conds'] ) ? (array)$query['join_conds'] : [] );
114 if ( $limit !== false ) {
115 if ( $offset !== false ) {
116 // We need to increase the limit by the offset rather than
117 // using the offset directly, otherwise it'll skip incorrectly
118 // in the subqueries.
119 $sqb->limit( intval( $limit ) + intval( $offset ) );
120 } else {
121 $sqb->limit( intval( $limit ) );
122 }
123 }
124
125 $order = $this->getOrderFields();
126 if ( $this->sortDescending() ) {
127 foreach ( $order as &$field ) {
128 $field .= ' DESC';
129 }
130 }
131
132 $uqb = $dbr->newUnionQueryBuilder()->all();
133 foreach ( $namespaces as $namespace ) {
134 $nsSqb = clone $sqb;
135 $nsSqb->orderBy( $order );
136 $nsSqb->andWhere( [ 'page_namespace' => $namespace ] );
137 $uqb->add( $nsSqb );
138 }
139
140 if ( $limit !== false ) {
141 $uqb->limit( intval( $limit ) );
142 }
143 if ( $offset !== false ) {
144 $uqb->offset( intval( $offset ) );
145 }
146 $orderBy = 'value';
147 if ( $this->sortDescending() ) {
148 $orderBy .= ' DESC';
149 }
150 $uqb->orderBy( $orderBy );
151 return $uqb->caller( $fname )->fetchResultSet();
152 }
153
154 protected function getOrderFields() {
155 return [ 'page_len' ];
156 }
157
162 public function preprocessResults( $db, $res ) {
163 $this->executeLBFromResultWrapper( $res );
164 }
165
166 protected function sortDescending() {
167 return false;
168 }
169
175 public function formatResult( $skin, $result ) {
176 $title = Title::makeTitleSafe( $result->namespace, $result->title );
177 if ( !$title ) {
178 return Html::element( 'span', [ 'class' => 'mw-invalidtitle' ],
179 Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) );
180 }
181
182 $linkRenderer = $this->getLinkRenderer();
183 $hlink = $linkRenderer->makeKnownLink(
184 $title,
185 $this->msg( 'hist' )->text(),
186 [],
187 [ 'action' => 'history' ]
188 );
189 $hlinkInParentheses = $this->msg( 'parentheses' )->rawParams( $hlink )->escaped();
190
191 if ( $this->isCached() ) {
192 $plink = $linkRenderer->makeLink( $title );
193 $exists = $title->exists();
194 } else {
195 $plink = $linkRenderer->makeKnownLink( $title );
196 $exists = true;
197 }
198 $contentLanguage = $this->getContentLanguage();
199 $bdiAttrs = [
200 'dir' => $contentLanguage->getDir(),
201 'lang' => $contentLanguage->getHtmlCode(),
202 ];
203 $plink = Html::rawElement( 'bdi', $bdiAttrs, $plink );
204 $size = $this->msg( 'nbytes' )->numParams( $result->value )->escaped();
205 $result = "{$hlinkInParentheses} {$plink} [{$size}]";
206
207 return $exists ? $result : Html::rawElement( 'del', [], $result );
208 }
209
210 protected function getGroupName() {
211 return 'maintenance';
212 }
213}
214
219class_alias( SpecialShortPages::class, 'SpecialShortPages' );
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Some internal bits split of from Skin.php.
Definition Linker.php:63
A class containing constants representing the names of configuration variables.
const ShortPagesNamespaceExclusions
Name constant for the ShortPagesNamespaceExclusions setting, for use with Config::get()
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:87
setDatabaseProvider(IConnectionProvider $databaseProvider)
int $offset
The offset and limit in use, as passed to the query() function.
Definition QueryPage.php:92
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
getRecacheDB()
Get a DB connection to be used for slow recache queries.
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getContentLanguage()
Shortcut to get content language.
List of the shortest pages in the database.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
__construct(NamespaceInfo $namespaceInfo, IConnectionProvider $dbProvider, LinkBatchFactory $linkBatchFactory)
reallyDoQuery( $limit, $offset=false)
Run the query and return the result.
getOrderFields()
Subclasses return an array of fields to order by here.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
sortDescending()
Override to sort by increasing values.
isSyndicated()
Sometimes we don't want to build rss / atom feeds.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Represents a title within MediaWiki.
Definition Title.php:78
The base class for all skins.
Definition Skin.php:64
Provide primary and replica IDatabase connections.
Interface to a relational database.
Definition IDatabase.php:48
Result wrapper for grabbing data queried from an IDatabase object.
element(SerializerNode $parent, SerializerNode $node, $contents)