MediaWiki  master
ApiQueryAllPages.php
Go to the documentation of this file.
1 <?php
23 
30 
31  public function __construct( ApiQuery $query, $moduleName ) {
32  parent::__construct( $query, $moduleName, 'ap' );
33  }
34 
35  public function execute() {
36  $this->run();
37  }
38 
39  public function getCacheMode( $params ) {
40  return 'public';
41  }
42 
47  public function executeGenerator( $resultPageSet ) {
48  if ( $resultPageSet->isResolvingRedirects() ) {
49  $this->dieWithError( 'apierror-allpages-generator-redirects', 'params' );
50  }
51 
52  $this->run( $resultPageSet );
53  }
54 
59  private function run( $resultPageSet = null ) {
60  $db = $this->getDB();
61 
62  $params = $this->extractRequestParams();
63 
64  // Page filters
65  $this->addTables( 'page' );
66 
67  if ( !is_null( $params['continue'] ) ) {
68  $cont = explode( '|', $params['continue'] );
69  $this->dieContinueUsageIf( count( $cont ) != 1 );
70  $op = $params['dir'] == 'descending' ? '<' : '>';
71  $cont_from = $db->addQuotes( $cont[0] );
72  $this->addWhere( "page_title $op= $cont_from" );
73  }
74 
75  $miserMode = $this->getConfig()->get( 'MiserMode' );
76  if ( !$miserMode ) {
77  if ( $params['filterredir'] == 'redirects' ) {
78  $this->addWhereFld( 'page_is_redirect', 1 );
79  } elseif ( $params['filterredir'] == 'nonredirects' ) {
80  $this->addWhereFld( 'page_is_redirect', 0 );
81  }
82  }
83 
84  $this->addWhereFld( 'page_namespace', $params['namespace'] );
85  $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
86  $from = ( $params['from'] === null
87  ? null
88  : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
89  $to = ( $params['to'] === null
90  ? null
91  : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
92  $this->addWhereRange( 'page_title', $dir, $from, $to );
93 
94  if ( isset( $params['prefix'] ) ) {
95  $this->addWhere( 'page_title' . $db->buildLike(
96  $this->titlePartToKey( $params['prefix'], $params['namespace'] ),
97  $db->anyString() ) );
98  }
99 
100  if ( is_null( $resultPageSet ) ) {
101  $selectFields = [
102  'page_namespace',
103  'page_title',
104  'page_id'
105  ];
106  } else {
107  $selectFields = $resultPageSet->getPageTableFields();
108  }
109 
110  $miserModeFilterRedirValue = null;
111  $miserModeFilterRedir = $miserMode && $params['filterredir'] !== 'all';
112  if ( $miserModeFilterRedir ) {
113  $selectFields[] = 'page_is_redirect';
114 
115  if ( $params['filterredir'] == 'redirects' ) {
116  $miserModeFilterRedirValue = 1;
117  } elseif ( $params['filterredir'] == 'nonredirects' ) {
118  $miserModeFilterRedirValue = 0;
119  }
120  }
121 
122  $this->addFields( $selectFields );
123  $forceNameTitleIndex = true;
124  if ( isset( $params['minsize'] ) ) {
125  $this->addWhere( 'page_len>=' . (int)$params['minsize'] );
126  $forceNameTitleIndex = false;
127  }
128 
129  if ( isset( $params['maxsize'] ) ) {
130  $this->addWhere( 'page_len<=' . (int)$params['maxsize'] );
131  $forceNameTitleIndex = false;
132  }
133 
134  // Page protection filtering
135  if ( $params['prtype'] || $params['prexpiry'] != 'all' ) {
136  $this->addTables( 'page_restrictions' );
137  $this->addWhere( 'page_id=pr_page' );
138  $this->addWhere( "pr_expiry > {$db->addQuotes( $db->timestamp() )} OR pr_expiry IS NULL" );
139 
140  if ( $params['prtype'] ) {
141  $this->addWhereFld( 'pr_type', $params['prtype'] );
142 
143  if ( isset( $params['prlevel'] ) ) {
144  // Remove the empty string and '*' from the prlevel array
145  $prlevel = array_diff( $params['prlevel'], [ '', '*' ] );
146 
147  if ( count( $prlevel ) ) {
148  $this->addWhereFld( 'pr_level', $prlevel );
149  }
150  }
151  if ( $params['prfiltercascade'] == 'cascading' ) {
152  $this->addWhereFld( 'pr_cascade', 1 );
153  } elseif ( $params['prfiltercascade'] == 'noncascading' ) {
154  $this->addWhereFld( 'pr_cascade', 0 );
155  }
156  }
157  $forceNameTitleIndex = false;
158 
159  if ( $params['prexpiry'] == 'indefinite' ) {
160  $this->addWhere( "pr_expiry = {$db->addQuotes( $db->getInfinity() )} OR pr_expiry IS NULL" );
161  } elseif ( $params['prexpiry'] == 'definite' ) {
162  $this->addWhere( "pr_expiry != {$db->addQuotes( $db->getInfinity() )}" );
163  }
164 
165  $this->addOption( 'DISTINCT' );
166  } elseif ( isset( $params['prlevel'] ) ) {
167  $this->dieWithError(
168  [ 'apierror-invalidparammix-mustusewith', 'prlevel', 'prtype' ], 'invalidparammix'
169  );
170  }
171 
172  if ( $params['filterlanglinks'] == 'withoutlanglinks' ) {
173  $this->addTables( 'langlinks' );
174  $this->addJoinConds( [ 'langlinks' => [ 'LEFT JOIN', 'page_id=ll_from' ] ] );
175  $this->addWhere( 'll_from IS NULL' );
176  $forceNameTitleIndex = false;
177  } elseif ( $params['filterlanglinks'] == 'withlanglinks' ) {
178  $this->addTables( 'langlinks' );
179  $this->addWhere( 'page_id=ll_from' );
180  $this->addOption( 'STRAIGHT_JOIN' );
181 
182  // MySQL filesorts if we use a GROUP BY that works with the rules
183  // in the 1992 SQL standard (it doesn't like having the
184  // constant-in-WHERE page_namespace column in there). Using the
185  // 1999 rules works fine, but that breaks other DBs. Sigh.
187  $dbType = $db->getType();
188  if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
189  // Ignore the rules, or 1999 rules if you count unique keys
190  // over non-NULL columns as satisfying the requirement for
191  // "functional dependency" and don't require including
192  // constant-in-WHERE columns in the GROUP BY.
193  $this->addOption( 'GROUP BY', [ 'page_title' ] );
194  } elseif ( $dbType === 'postgres' && $db->getServerVersion() >= 9.1 ) {
195  // 1999 rules only counting primary keys
196  $this->addOption( 'GROUP BY', [ 'page_title', 'page_id' ] );
197  } else {
198  // 1992 rules
199  $this->addOption( 'GROUP BY', $selectFields );
200  }
201 
202  $forceNameTitleIndex = false;
203  }
204 
205  if ( $forceNameTitleIndex ) {
206  $this->addOption( 'USE INDEX', 'name_title' );
207  }
208 
209  $limit = $params['limit'];
210  $this->addOption( 'LIMIT', $limit + 1 );
211  $res = $this->select( __METHOD__ );
212 
213  // Get gender information
214  $services = MediaWikiServices::getInstance();
215  if ( $services->getNamespaceInfo()->hasGenderDistinction( $params['namespace'] ) ) {
216  $users = [];
217  foreach ( $res as $row ) {
218  $users[] = $row->page_title;
219  }
220  $services->getGenderCache()->doQuery( $users, __METHOD__ );
221  $res->rewind(); // reset
222  }
223 
224  $count = 0;
225  $result = $this->getResult();
226  foreach ( $res as $row ) {
227  if ( ++$count > $limit ) {
228  // We've reached the one extra which shows that there are
229  // additional pages to be had. Stop here...
230  $this->setContinueEnumParameter( 'continue', $row->page_title );
231  break;
232  }
233 
234  if ( $miserModeFilterRedir && (int)$row->page_is_redirect !== $miserModeFilterRedirValue ) {
235  // Filter implemented in PHP due to being in Miser Mode
236  continue;
237  }
238 
239  if ( is_null( $resultPageSet ) ) {
240  $title = Title::makeTitle( $row->page_namespace, $row->page_title );
241  $vals = [
242  'pageid' => (int)$row->page_id,
243  'ns' => (int)$title->getNamespace(),
244  'title' => $title->getPrefixedText()
245  ];
246  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
247  if ( !$fit ) {
248  $this->setContinueEnumParameter( 'continue', $row->page_title );
249  break;
250  }
251  } else {
252  $resultPageSet->processDbRow( $row );
253  }
254  }
255 
256  if ( is_null( $resultPageSet ) ) {
257  $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'p' );
258  }
259  }
260 
261  public function getAllowedParams() {
262  $ret = [
263  'from' => null,
264  'continue' => [
265  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
266  ],
267  'to' => null,
268  'prefix' => null,
269  'namespace' => [
271  ApiBase::PARAM_TYPE => 'namespace',
272  ],
273  'filterredir' => [
274  ApiBase::PARAM_DFLT => 'all',
275  ApiBase::PARAM_TYPE => [
276  'all',
277  'redirects',
278  'nonredirects'
279  ]
280  ],
281  'minsize' => [
282  ApiBase::PARAM_TYPE => 'integer',
283  ],
284  'maxsize' => [
285  ApiBase::PARAM_TYPE => 'integer',
286  ],
287  'prtype' => [
288  ApiBase::PARAM_TYPE => Title::getFilteredRestrictionTypes( true ),
290  ],
291  'prlevel' => [
292  ApiBase::PARAM_TYPE => $this->getConfig()->get( 'RestrictionLevels' ),
294  ],
295  'prfiltercascade' => [
296  ApiBase::PARAM_DFLT => 'all',
297  ApiBase::PARAM_TYPE => [
298  'cascading',
299  'noncascading',
300  'all'
301  ],
302  ],
303  'limit' => [
304  ApiBase::PARAM_DFLT => 10,
305  ApiBase::PARAM_TYPE => 'limit',
306  ApiBase::PARAM_MIN => 1,
309  ],
310  'dir' => [
311  ApiBase::PARAM_DFLT => 'ascending',
312  ApiBase::PARAM_TYPE => [
313  'ascending',
314  'descending'
315  ]
316  ],
317  'filterlanglinks' => [
318  ApiBase::PARAM_TYPE => [
319  'withlanglinks',
320  'withoutlanglinks',
321  'all'
322  ],
323  ApiBase::PARAM_DFLT => 'all'
324  ],
325  'prexpiry' => [
326  ApiBase::PARAM_TYPE => [
327  'indefinite',
328  'definite',
329  'all'
330  ],
331  ApiBase::PARAM_DFLT => 'all'
332  ],
333  ];
334 
335  if ( $this->getConfig()->get( 'MiserMode' ) ) {
336  $ret['filterredir'][ApiBase::PARAM_HELP_MSG_APPEND] = [ 'api-help-param-limited-in-miser-mode' ];
337  }
338 
339  return $ret;
340  }
341 
342  protected function getExamplesMessages() {
343  return [
344  'action=query&list=allpages&apfrom=B'
345  => 'apihelp-query+allpages-example-b',
346  'action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info'
347  => 'apihelp-query+allpages-example-generator',
348  'action=query&generator=allpages&gaplimit=2&' .
349  'gapfilterredir=nonredirects&gapfrom=Re&prop=revisions&rvprop=content'
350  => 'apihelp-query+allpages-example-generator-revisions',
351  ];
352  }
353 
354  public function getHelpUrls() {
355  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allpages';
356  }
357 }
static getFilteredRestrictionTypes( $exists=true)
Get a filtered list of all restriction types supported by this wiki.
Definition: Title.php:2453
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below...
Definition: ApiBase.php:94
getDB()
Get the Query database connection (read-only)
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:261
getResult()
Get the result object.
Definition: ApiBase.php:640
const NS_MAIN
Definition: Defines.php:60
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
run( $resultPageSet=null)
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
executeGenerator( $resultPageSet)
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:259
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:97
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2005
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user...
Definition: ApiBase.php:761
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
Query module to enumerate all available pages.
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:138
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
dieContinueUsageIf( $condition)
Die with the &#39;badcontinue&#39; error.
Definition: ApiBase.php:2199
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
addFields( $value)
Add a set of fields to select to the internal array.
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right, for PARAM_TYPE &#39;limit&#39;.
Definition: ApiBase.php:103
This is the main query class.
Definition: ApiQuery.php:37
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter...
Definition: ApiBase.php:131
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
addWhere( $value)
Add a set of WHERE clauses to the internal array.
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
__construct(ApiQuery $query, $moduleName)
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:106
return true
Definition: router.php:92
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction...