MediaWiki master
SpecialRandomPage.php
Go to the documentation of this file.
1<?php
25namespace MediaWiki\Specials;
26
31
38 private $namespaces; // namespaces to select pages from
39 protected $isRedir = false; // should the result be a redirect?
40 protected $extra = []; // Extra SQL statements
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()->getValues(), $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 $redirect = $this->isRedirect() ? 1 : 0;
191 $tables = [ 'page' ];
192 $conds = array_merge( [
193 'page_namespace' => $this->namespaces,
194 'page_is_redirect' => $redirect,
195 'page_random >= ' . $randstr
196 ], $this->extra );
197 $joinConds = [];
198
199 // Allow extensions to modify the query
200 $this->getHookRunner()->onRandomPageQuery( $tables, $conds, $joinConds );
201
202 return [
203 'tables' => $tables,
204 'fields' => [ 'page_title', 'page_namespace' ],
205 'conds' => $conds,
206 'options' => [
207 'ORDER BY' => 'page_random',
208 'LIMIT' => 1,
209 ],
210 'join_conds' => $joinConds
211 ];
212 }
213
214 private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
215 $dbr = $this->dbProvider->getReplicaDatabase();
216
217 $query = $this->getQueryInfo( $randstr );
218 $res = $dbr->select(
219 $query['tables'],
220 $query['fields'],
221 $query['conds'],
222 $fname,
223 $query['options'],
224 $query['join_conds']
225 );
226
227 return $res->fetchObject();
228 }
229
230 protected function getGroupName() {
231 return 'redirects';
232 }
233}
234
239class_alias( SpecialRandomPage::class, 'SpecialRandomPage' );
const NS_MAIN
Definition Defines.php:64
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.
Special page to direct the user to a random page.
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:78
Provide primary and replica IDatabase connections.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...