MediaWiki master
SpecialRandomPage.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
27
36 private $namespaces;
38 protected $isRedir = false;
40 protected $extra = [];
41
42 private IConnectionProvider $dbProvider;
43
48 public function __construct(
49 IConnectionProvider $dbProvider,
50 NamespaceInfo $nsInfo
51 ) {
52 parent::__construct( 'Randompage' );
53 $this->dbProvider = $dbProvider;
54 $this->namespaces = $nsInfo->getContentNamespaces();
55 }
56
57 public function getNamespaces() {
58 return $this->namespaces;
59 }
60
61 public function setNamespace( $ns ) {
62 if ( !$this->isValidNS( $ns ) ) {
63 $ns = NS_MAIN;
64 }
65 $this->namespaces = [ $ns ];
66 }
67
68 private function isValidNS( $ns ) {
69 return $ns !== false && $ns >= 0;
70 }
71
72 // select redirects instead of normal pages?
73 public function isRedirect() {
74 return $this->isRedir;
75 }
76
77 public function execute( $par ) {
78 $this->parsePar( $par );
79
80 $title = $this->getRandomTitle();
81
82 if ( $title === null ) {
83 $this->setHeaders();
84 // Message: randompage-nopages, randomredirect-nopages
85 $this->getOutput()->addWikiMsg( strtolower( $this->getName() ) . '-nopages',
86 $this->getNsList(), count( $this->namespaces ) );
87
88 return;
89 }
90
91 $redirectParam = $this->isRedirect() ? [ 'redirect' => 'no' ] : [];
92 $query = array_merge( $this->getRequest()->getQueryValues(), $redirectParam );
93 unset( $query['title'] );
94 $this->getOutput()->redirect( $title->getFullURL( $query ) );
95 }
96
102 private function parsePar( $par ) {
103 // Testing for stringiness since we want to catch
104 // the empty string to mean main namespace only.
105 if ( is_string( $par ) ) {
106 $ns = $this->getContentLanguage()->getNsIndex( $par );
107 if ( $ns === false && strpos( $par, ',' ) !== false ) {
108 $nsList = [];
109 // Comma separated list
110 $parSplit = explode( ',', $par );
111 foreach ( $parSplit as $potentialNs ) {
112 $ns = $this->getContentLanguage()->getNsIndex( $potentialNs );
113 if ( $this->isValidNS( $ns ) ) {
114 $nsList[] = $ns;
115 }
116 // Remove duplicate values, and re-index array
117 $nsList = array_unique( $nsList );
118 $nsList = array_values( $nsList );
119 if ( $nsList !== [] ) {
120 $this->namespaces = $nsList;
121 }
122 }
123 } else {
124 // Note, that the case of $par being something
125 // like "main" which is not a namespace, falls
126 // through to here, and sets NS_MAIN, allowing
127 // Special:Random/main or Special:Random/article
128 // to work as expected.
129 $this->setNamespace( $this->getContentLanguage()->getNsIndex( $par ) );
130 }
131 }
132 }
133
139 private function getNsList() {
140 $contLang = $this->getContentLanguage();
141 $nsNames = [];
142 foreach ( $this->namespaces as $n ) {
143 if ( $n === NS_MAIN ) {
144 $nsNames[] = $this->msg( 'blanknamespace' )->plain();
145 } else {
146 $nsNames[] = $contLang->getNsText( $n );
147 }
148 }
149
150 return $contLang->commaList( $nsNames );
151 }
152
157 public function getRandomTitle() {
158 $randstr = wfRandom();
159 $title = null;
160
161 if ( !$this->getHookRunner()->onSpecialRandomGetRandomTitle(
162 $randstr, $this->isRedir, $this->namespaces,
163 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
164 $this->extra, $title )
165 ) {
166 return $title;
167 }
168
169 $row = $this->selectRandomPageFromDB( $randstr, __METHOD__ );
170
171 /* If we picked a value that was higher than any in
172 * the DB, wrap around and select the page with the
173 * lowest value instead! One might think this would
174 * skew the distribution, but in fact it won't cause
175 * any more bias than what the page_random scheme
176 * causes anyway. Trust me, I'm a mathematician. :)
177 */
178 if ( !$row ) {
179 $row = $this->selectRandomPageFromDB( 0, __METHOD__ );
180 }
181
182 if ( $row ) {
183 return Title::makeTitleSafe( $row->page_namespace, $row->page_title );
184 }
185
186 return null;
187 }
188
189 protected function getQueryInfo( $randstr ) {
190 $dbr = $this->dbProvider->getReplicaDatabase();
191 $redirect = $this->isRedirect() ? 1 : 0;
192 $tables = [ 'page' ];
193 $conds = array_merge( [
194 'page_namespace' => $this->namespaces,
195 'page_is_redirect' => $redirect,
196 $dbr->expr( 'page_random', '>=', $randstr ),
197 ], $this->extra );
198 $joinConds = [];
199
200 // Allow extensions to modify the query
201 $this->getHookRunner()->onRandomPageQuery( $tables, $conds, $joinConds );
202
203 return [
204 'tables' => $tables,
205 'fields' => [ 'page_title', 'page_namespace' ],
206 'conds' => $conds,
207 'options' => [
208 'ORDER BY' => 'page_random',
209 'LIMIT' => 1,
210 ],
211 'join_conds' => $joinConds
212 ];
213 }
214
215 private function selectRandomPageFromDB( $randstr, $fname ) {
216 $dbr = $this->dbProvider->getReplicaDatabase();
217
218 $query = $this->getQueryInfo( $randstr );
219 return $dbr->newSelectQueryBuilder()
220 ->queryInfo( $query )
221 ->caller( $fname )
222 ->fetchRow();
223 }
224
225 protected function getGroupName() {
226 return 'redirects';
227 }
228}
229
234class_alias( SpecialRandomPage::class, 'SpecialRandomPage' );
const NS_MAIN
Definition Defines.php:65
wfRandom()
Get a random decimal value in the domain of [0, 1), in a way not likely to give duplicate values for ...
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getContentLanguage()
Shortcut to get content language.
getName()
Get the canonical, unlocalized name of this special page without namespace.
execute( $par)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
__construct(IConnectionProvider $dbProvider, NamespaceInfo $nsInfo)
bool $isRedir
should the result be a redirect?
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Represents a title within MediaWiki.
Definition Title.php:78
Provide primary and replica IDatabase connections.