MediaWiki master
SpecialRandomPage.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
27
35 private $namespaces; // namespaces to select pages from
36 protected $isRedir = false; // should the result be a redirect?
37 protected $extra = []; // Extra SQL statements
38
39 private IConnectionProvider $dbProvider;
40
45 public function __construct(
46 IConnectionProvider $dbProvider,
47 NamespaceInfo $nsInfo
48 ) {
49 parent::__construct( 'Randompage' );
50 $this->dbProvider = $dbProvider;
51 $this->namespaces = $nsInfo->getContentNamespaces();
52 }
53
54 public function getNamespaces() {
55 return $this->namespaces;
56 }
57
58 public function setNamespace( $ns ) {
59 if ( !$this->isValidNS( $ns ) ) {
60 $ns = NS_MAIN;
61 }
62 $this->namespaces = [ $ns ];
63 }
64
65 private function isValidNS( $ns ) {
66 return $ns !== false && $ns >= 0;
67 }
68
69 // select redirects instead of normal pages?
70 public function isRedirect() {
71 return $this->isRedir;
72 }
73
74 public function execute( $par ) {
75 $this->parsePar( $par );
76
77 $title = $this->getRandomTitle();
78
79 if ( $title === null ) {
80 $this->setHeaders();
81 // Message: randompage-nopages, randomredirect-nopages
82 $this->getOutput()->addWikiMsg( strtolower( $this->getName() ) . '-nopages',
83 $this->getNsList(), count( $this->namespaces ) );
84
85 return;
86 }
87
88 $redirectParam = $this->isRedirect() ? [ 'redirect' => 'no' ] : [];
89 $query = array_merge( $this->getRequest()->getValues(), $redirectParam );
90 unset( $query['title'] );
91 $this->getOutput()->redirect( $title->getFullURL( $query ) );
92 }
93
99 private function parsePar( $par ) {
100 // Testing for stringiness since we want to catch
101 // the empty string to mean main namespace only.
102 if ( is_string( $par ) ) {
103 $ns = $this->getContentLanguage()->getNsIndex( $par );
104 if ( $ns === false && strpos( $par, ',' ) !== false ) {
105 $nsList = [];
106 // Comma separated list
107 $parSplit = explode( ',', $par );
108 foreach ( $parSplit as $potentialNs ) {
109 $ns = $this->getContentLanguage()->getNsIndex( $potentialNs );
110 if ( $this->isValidNS( $ns ) ) {
111 $nsList[] = $ns;
112 }
113 // Remove duplicate values, and re-index array
114 $nsList = array_unique( $nsList );
115 $nsList = array_values( $nsList );
116 if ( $nsList !== [] ) {
117 $this->namespaces = $nsList;
118 }
119 }
120 } else {
121 // Note, that the case of $par being something
122 // like "main" which is not a namespace, falls
123 // through to here, and sets NS_MAIN, allowing
124 // Special:Random/main or Special:Random/article
125 // to work as expected.
126 $this->setNamespace( $this->getContentLanguage()->getNsIndex( $par ) );
127 }
128 }
129 }
130
136 private function getNsList() {
137 $contLang = $this->getContentLanguage();
138 $nsNames = [];
139 foreach ( $this->namespaces as $n ) {
140 if ( $n === NS_MAIN ) {
141 $nsNames[] = $this->msg( 'blanknamespace' )->plain();
142 } else {
143 $nsNames[] = $contLang->getNsText( $n );
144 }
145 }
146
147 return $contLang->commaList( $nsNames );
148 }
149
154 public function getRandomTitle() {
155 $randstr = wfRandom();
156 $title = null;
157
158 if ( !$this->getHookRunner()->onSpecialRandomGetRandomTitle(
159 $randstr, $this->isRedir, $this->namespaces,
160 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
161 $this->extra, $title )
162 ) {
163 return $title;
164 }
165
166 $row = $this->selectRandomPageFromDB( $randstr, __METHOD__ );
167
168 /* If we picked a value that was higher than any in
169 * the DB, wrap around and select the page with the
170 * lowest value instead! One might think this would
171 * skew the distribution, but in fact it won't cause
172 * any more bias than what the page_random scheme
173 * causes anyway. Trust me, I'm a mathematician. :)
174 */
175 if ( !$row ) {
176 $row = $this->selectRandomPageFromDB( 0, __METHOD__ );
177 }
178
179 if ( $row ) {
180 return Title::makeTitleSafe( $row->page_namespace, $row->page_title );
181 }
182
183 return null;
184 }
185
186 protected function getQueryInfo( $randstr ) {
187 $dbr = $this->dbProvider->getReplicaDatabase();
188 $redirect = $this->isRedirect() ? 1 : 0;
189 $tables = [ 'page' ];
190 $conds = array_merge( [
191 'page_namespace' => $this->namespaces,
192 'page_is_redirect' => $redirect,
193 $dbr->expr( 'page_random', '>=', $randstr ),
194 ], $this->extra );
195 $joinConds = [];
196
197 // Allow extensions to modify the query
198 $this->getHookRunner()->onRandomPageQuery( $tables, $conds, $joinConds );
199
200 return [
201 'tables' => $tables,
202 'fields' => [ 'page_title', 'page_namespace' ],
203 'conds' => $conds,
204 'options' => [
205 'ORDER BY' => 'page_random',
206 'LIMIT' => 1,
207 ],
208 'join_conds' => $joinConds
209 ];
210 }
211
212 private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
213 $dbr = $this->dbProvider->getReplicaDatabase();
214
215 $query = $this->getQueryInfo( $randstr );
216 return $dbr->newSelectQueryBuilder()
217 ->queryInfo( $query )
218 ->caller( $fname )
219 ->fetchRow();
220 }
221
222 protected function getGroupName() {
223 return 'redirects';
224 }
225}
226
231class_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)
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:79
Provide primary and replica IDatabase connections.