MediaWiki  master
SpecialRandomPage.php
Go to the documentation of this file.
1 <?php
28 
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 
40  private $loadBalancer;
41 
46  public function __construct(
47  $loadBalancer = null,
48  NamespaceInfo $nsInfo = null
49  ) {
50  parent::__construct( is_string( $loadBalancer ) ? $loadBalancer : 'Randompage' );
51  // This class is extended and therefor fallback to global state - T265308
52  $services = MediaWikiServices::getInstance();
53  $this->loadBalancer = $loadBalancer instanceof ILoadBalancer
54  ? $loadBalancer
55  : $services->getDBLoadBalancer();
56  $nsInfo ??= $services->getNamespaceInfo();
57  $this->namespaces = $nsInfo->getContentNamespaces();
58  }
59 
60  public function getNamespaces() {
61  return $this->namespaces;
62  }
63 
64  public function setNamespace( $ns ) {
65  if ( !$ns || $ns < NS_MAIN ) {
66  $ns = NS_MAIN;
67  }
68  $this->namespaces = [ $ns ];
69  }
70 
71  // select redirects instead of normal pages?
72  public function isRedirect() {
73  return $this->isRedir;
74  }
75 
76  public function execute( $par ) {
77  if ( is_string( $par ) ) {
78  // Testing for stringiness since we want to catch
79  // the empty string to mean main namespace only.
80  $this->setNamespace( $this->getContentLanguage()->getNsIndex( $par ) );
81  }
82 
83  $title = $this->getRandomTitle();
84 
85  if ( $title === null ) {
86  $this->setHeaders();
87  // Message: randompage-nopages, randomredirect-nopages
88  $this->getOutput()->addWikiMsg( strtolower( $this->getName() ) . '-nopages',
89  $this->getNsList(), count( $this->namespaces ) );
90 
91  return;
92  }
93 
94  $redirectParam = $this->isRedirect() ? [ 'redirect' => 'no' ] : [];
95  $query = array_merge( $this->getRequest()->getValues(), $redirectParam );
96  unset( $query['title'] );
97  $this->getOutput()->redirect( $title->getFullURL( $query ) );
98  }
99 
105  private function getNsList() {
106  $contLang = $this->getContentLanguage();
107  $nsNames = [];
108  foreach ( $this->namespaces as $n ) {
109  if ( $n === NS_MAIN ) {
110  $nsNames[] = $this->msg( 'blanknamespace' )->plain();
111  } else {
112  $nsNames[] = $contLang->getNsText( $n );
113  }
114  }
115 
116  return $contLang->commaList( $nsNames );
117  }
118 
123  public function getRandomTitle() {
124  $randstr = wfRandom();
125  $title = null;
126 
127  if ( !$this->getHookRunner()->onSpecialRandomGetRandomTitle(
128  $randstr, $this->isRedir, $this->namespaces,
129  // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
130  $this->extra, $title )
131  ) {
132  return $title;
133  }
134 
135  $row = $this->selectRandomPageFromDB( $randstr, __METHOD__ );
136 
137  /* If we picked a value that was higher than any in
138  * the DB, wrap around and select the page with the
139  * lowest value instead! One might think this would
140  * skew the distribution, but in fact it won't cause
141  * any more bias than what the page_random scheme
142  * causes anyway. Trust me, I'm a mathematician. :)
143  */
144  if ( !$row ) {
145  $row = $this->selectRandomPageFromDB( "0", __METHOD__ );
146  }
147 
148  if ( $row ) {
149  return Title::makeTitleSafe( $row->page_namespace, $row->page_title );
150  }
151 
152  return null;
153  }
154 
155  protected function getQueryInfo( $randstr ) {
156  $redirect = $this->isRedirect() ? 1 : 0;
157  $tables = [ 'page' ];
158  $conds = array_merge( [
159  'page_namespace' => $this->namespaces,
160  'page_is_redirect' => $redirect,
161  'page_random >= ' . $randstr
162  ], $this->extra );
163  $joinConds = [];
164 
165  // Allow extensions to modify the query
166  $this->getHookRunner()->onRandomPageQuery( $tables, $conds, $joinConds );
167 
168  return [
169  'tables' => $tables,
170  'fields' => [ 'page_title', 'page_namespace' ],
171  'conds' => $conds,
172  'options' => [
173  'ORDER BY' => 'page_random',
174  'LIMIT' => 1,
175  ],
176  'join_conds' => $joinConds
177  ];
178  }
179 
180  private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
181  $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
182 
183  $query = $this->getQueryInfo( $randstr );
184  $res = $dbr->select(
185  $query['tables'],
186  $query['fields'],
187  $query['conds'],
188  $fname,
189  $query['options'],
190  $query['join_conds']
191  );
192 
193  return $res->fetchObject();
194  }
195 
196  protected function getGroupName() {
197  return 'redirects';
198  }
199 }
200 
205 class_alias( SpecialRandomPage::class, 'RandomPage' );
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 ...
Service locator for MediaWiki core services.
Represents a title within MediaWiki.
Definition: Title.php:82
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Parent class for all special pages.
Definition: SpecialPage.php:45
getName()
Get the name of this Special Page.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getRequest()
Get the WebRequest being used for this instance.
getContentLanguage()
Shortcut to get content language.
Special page to direct the user to a random page.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
execute( $par)
Default execute method Checks user permissions.
getRandomTitle()
Choose a random title.
__construct( $loadBalancer=null, NamespaceInfo $nsInfo=null)
This class is a delegate to ILBFactory for a given database cluster.
const DB_REPLICA
Definition: defines.php:26