MediaWiki REL1_39
ApiQueryAllPages.php
Go to the documentation of this file.
1<?php
27
34
36 private $namespaceInfo;
37
39 private $genderCache;
40
42 private $restrictionStore;
43
51 public function __construct(
52 ApiQuery $query,
53 $moduleName,
54 NamespaceInfo $namespaceInfo,
55 GenderCache $genderCache,
56 RestrictionStore $restrictionStore
57 ) {
58 parent::__construct( $query, $moduleName, 'ap' );
59 $this->namespaceInfo = $namespaceInfo;
60 $this->genderCache = $genderCache;
61 $this->restrictionStore = $restrictionStore;
62 }
63
64 public function execute() {
65 $this->run();
66 }
67
68 public function getCacheMode( $params ) {
69 return 'public';
70 }
71
76 public function executeGenerator( $resultPageSet ) {
77 if ( $resultPageSet->isResolvingRedirects() ) {
78 $this->dieWithError( 'apierror-allpages-generator-redirects', 'params' );
79 }
80
81 $this->run( $resultPageSet );
82 }
83
88 private function run( $resultPageSet = null ) {
89 $db = $this->getDB();
90
91 $params = $this->extractRequestParams();
92
93 // Page filters
94 $this->addTables( 'page' );
95
96 if ( $params['continue'] !== null ) {
97 $cont = explode( '|', $params['continue'] );
98 $this->dieContinueUsageIf( count( $cont ) != 1 );
99 $op = $params['dir'] == 'descending' ? '<' : '>';
100 $cont_from = $db->addQuotes( $cont[0] );
101 $this->addWhere( "page_title $op= $cont_from" );
102 }
103
104 $miserMode = $this->getConfig()->get( MainConfigNames::MiserMode );
105 if ( !$miserMode ) {
106 if ( $params['filterredir'] == 'redirects' ) {
107 $this->addWhereFld( 'page_is_redirect', 1 );
108 } elseif ( $params['filterredir'] == 'nonredirects' ) {
109 $this->addWhereFld( 'page_is_redirect', 0 );
110 }
111 }
112
113 $this->addWhereFld( 'page_namespace', $params['namespace'] );
114 $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
115 $from = ( $params['from'] === null
116 ? null
117 : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
118 $to = ( $params['to'] === null
119 ? null
120 : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
121 $this->addWhereRange( 'page_title', $dir, $from, $to );
122
123 if ( isset( $params['prefix'] ) ) {
124 $this->addWhere( 'page_title' . $db->buildLike(
125 $this->titlePartToKey( $params['prefix'], $params['namespace'] ),
126 $db->anyString() ) );
127 }
128
129 if ( $resultPageSet === null ) {
130 $selectFields = [
131 'page_namespace',
132 'page_title',
133 'page_id'
134 ];
135 } else {
136 $selectFields = $resultPageSet->getPageTableFields();
137 }
138
139 $miserModeFilterRedirValue = null;
140 $miserModeFilterRedir = $miserMode && $params['filterredir'] !== 'all';
141 if ( $miserModeFilterRedir ) {
142 $selectFields[] = 'page_is_redirect';
143
144 if ( $params['filterredir'] == 'redirects' ) {
145 $miserModeFilterRedirValue = 1;
146 } elseif ( $params['filterredir'] == 'nonredirects' ) {
147 $miserModeFilterRedirValue = 0;
148 }
149 }
150
151 $this->addFields( $selectFields );
152 $forceNameTitleIndex = true;
153 if ( isset( $params['minsize'] ) ) {
154 $this->addWhere( 'page_len>=' . (int)$params['minsize'] );
155 $forceNameTitleIndex = false;
156 }
157
158 if ( isset( $params['maxsize'] ) ) {
159 $this->addWhere( 'page_len<=' . (int)$params['maxsize'] );
160 $forceNameTitleIndex = false;
161 }
162
163 // Page protection filtering
164 if ( $params['prtype'] || $params['prexpiry'] != 'all' ) {
165 $this->addTables( 'page_restrictions' );
166 $this->addWhere( 'page_id=pr_page' );
167 $this->addWhere( "pr_expiry > {$db->addQuotes( $db->timestamp() )} OR pr_expiry IS NULL" );
168
169 if ( $params['prtype'] ) {
170 $this->addWhereFld( 'pr_type', $params['prtype'] );
171
172 if ( isset( $params['prlevel'] ) ) {
173 // Remove the empty string and '*' from the prlevel array
174 $prlevel = array_diff( $params['prlevel'], [ '', '*' ] );
175
176 if ( count( $prlevel ) ) {
177 $this->addWhereFld( 'pr_level', $prlevel );
178 }
179 }
180 if ( $params['prfiltercascade'] == 'cascading' ) {
181 $this->addWhereFld( 'pr_cascade', 1 );
182 } elseif ( $params['prfiltercascade'] == 'noncascading' ) {
183 $this->addWhereFld( 'pr_cascade', 0 );
184 }
185 }
186 $forceNameTitleIndex = false;
187
188 if ( $params['prexpiry'] == 'indefinite' ) {
189 $this->addWhere( "pr_expiry = {$db->addQuotes( $db->getInfinity() )} OR pr_expiry IS NULL" );
190 } elseif ( $params['prexpiry'] == 'definite' ) {
191 $this->addWhere( "pr_expiry != {$db->addQuotes( $db->getInfinity() )}" );
192 }
193
194 $this->addOption( 'DISTINCT' );
195 } elseif ( isset( $params['prlevel'] ) ) {
196 $this->dieWithError(
197 [ 'apierror-invalidparammix-mustusewith', 'prlevel', 'prtype' ], 'invalidparammix'
198 );
199 }
200
201 if ( $params['filterlanglinks'] == 'withoutlanglinks' ) {
202 $this->addTables( 'langlinks' );
203 $this->addJoinConds( [ 'langlinks' => [ 'LEFT JOIN', 'page_id=ll_from' ] ] );
204 $this->addWhere( 'll_from IS NULL' );
205 $forceNameTitleIndex = false;
206 } elseif ( $params['filterlanglinks'] == 'withlanglinks' ) {
207 $this->addTables( 'langlinks' );
208 $this->addWhere( 'page_id=ll_from' );
209 $this->addOption( 'STRAIGHT_JOIN' );
210
211 // MySQL filesorts if we use a GROUP BY that works with the rules
212 // in the 1992 SQL standard (it doesn't like having the
213 // constant-in-WHERE page_namespace column in there). Using the
214 // 1999 rules works fine, but that breaks other DBs. Sigh.
215 // @todo Once we drop support for 1992-rule DBs, we can simplify this.
216 $dbType = $db->getType();
217 if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
218 // Ignore the rules, or 1999 rules if you count unique keys
219 // over non-NULL columns as satisfying the requirement for
220 // "functional dependency" and don't require including
221 // constant-in-WHERE columns in the GROUP BY.
222 $this->addOption( 'GROUP BY', [ 'page_title' ] );
223 } elseif ( $dbType === 'postgres' && $db->getServerVersion() >= 9.1 ) {
224 // 1999 rules only counting primary keys
225 $this->addOption( 'GROUP BY', [ 'page_title', 'page_id' ] );
226 } else {
227 // 1992 rules
228 $this->addOption( 'GROUP BY', $selectFields );
229 }
230
231 $forceNameTitleIndex = false;
232 }
233
234 if ( $forceNameTitleIndex ) {
235 $this->addOption( 'USE INDEX', 'page_name_title' );
236 }
237
238 $limit = $params['limit'];
239 $this->addOption( 'LIMIT', $limit + 1 );
240 $res = $this->select( __METHOD__ );
241
242 // Get gender information
243 if ( $this->namespaceInfo->hasGenderDistinction( $params['namespace'] ) ) {
244 $users = [];
245 foreach ( $res as $row ) {
246 $users[] = $row->page_title;
247 }
248 $this->genderCache->doQuery( $users, __METHOD__ );
249 $res->rewind(); // reset
250 }
251
252 $count = 0;
253 $result = $this->getResult();
254 foreach ( $res as $row ) {
255 if ( ++$count > $limit ) {
256 // We've reached the one extra which shows that there are
257 // additional pages to be had. Stop here...
258 $this->setContinueEnumParameter( 'continue', $row->page_title );
259 break;
260 }
261
262 if ( $miserModeFilterRedir && (int)$row->page_is_redirect !== $miserModeFilterRedirValue ) {
263 // Filter implemented in PHP due to being in Miser Mode
264 continue;
265 }
266
267 if ( $resultPageSet === null ) {
268 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
269 $vals = [
270 'pageid' => (int)$row->page_id,
271 'ns' => $title->getNamespace(),
272 'title' => $title->getPrefixedText()
273 ];
274 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
275 if ( !$fit ) {
276 $this->setContinueEnumParameter( 'continue', $row->page_title );
277 break;
278 }
279 } else {
280 $resultPageSet->processDbRow( $row );
281 }
282 }
283
284 if ( $resultPageSet === null ) {
285 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'p' );
286 }
287 }
288
289 public function getAllowedParams() {
290 $ret = [
291 'from' => null,
292 'continue' => [
293 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
294 ],
295 'to' => null,
296 'prefix' => null,
297 'namespace' => [
298 ParamValidator::PARAM_DEFAULT => NS_MAIN,
299 ParamValidator::PARAM_TYPE => 'namespace',
300 ],
301 'filterredir' => [
302 ParamValidator::PARAM_DEFAULT => 'all',
303 ParamValidator::PARAM_TYPE => [
304 'all',
305 'redirects',
306 'nonredirects'
307 ]
308 ],
309 'minsize' => [
310 ParamValidator::PARAM_TYPE => 'integer',
311 ],
312 'maxsize' => [
313 ParamValidator::PARAM_TYPE => 'integer',
314 ],
315 'prtype' => [
316 ParamValidator::PARAM_TYPE => $this->restrictionStore->listAllRestrictionTypes( true ),
317 ParamValidator::PARAM_ISMULTI => true
318 ],
319 'prlevel' => [
320 ParamValidator::PARAM_TYPE =>
321 $this->getConfig()->get( MainConfigNames::RestrictionLevels ),
322 ParamValidator::PARAM_ISMULTI => true
323 ],
324 'prfiltercascade' => [
325 ParamValidator::PARAM_DEFAULT => 'all',
326 ParamValidator::PARAM_TYPE => [
327 'cascading',
328 'noncascading',
329 'all'
330 ],
331 ],
332 'limit' => [
333 ParamValidator::PARAM_DEFAULT => 10,
334 ParamValidator::PARAM_TYPE => 'limit',
335 IntegerDef::PARAM_MIN => 1,
336 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
337 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
338 ],
339 'dir' => [
340 ParamValidator::PARAM_DEFAULT => 'ascending',
341 ParamValidator::PARAM_TYPE => [
342 'ascending',
343 'descending'
344 ]
345 ],
346 'filterlanglinks' => [
347 ParamValidator::PARAM_TYPE => [
348 'withlanglinks',
349 'withoutlanglinks',
350 'all'
351 ],
352 ParamValidator::PARAM_DEFAULT => 'all'
353 ],
354 'prexpiry' => [
355 ParamValidator::PARAM_TYPE => [
356 'indefinite',
357 'definite',
358 'all'
359 ],
360 ParamValidator::PARAM_DEFAULT => 'all',
362 ],
363 ];
364
365 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
366 $ret['filterredir'][ApiBase::PARAM_HELP_MSG_APPEND] = [ 'api-help-param-limited-in-miser-mode' ];
367 }
368
369 return $ret;
370 }
371
372 protected function getExamplesMessages() {
373 return [
374 'action=query&list=allpages&apfrom=B'
375 => 'apihelp-query+allpages-example-b',
376 'action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info'
377 => 'apihelp-query+allpages-example-generator',
378 'action=query&generator=allpages&gaplimit=2&' .
379 'gapfilterredir=nonredirects&gapfrom=Re&prop=revisions&rvprop=content'
380 => 'apihelp-query+allpages-example-generator-revisions',
381 ];
382 }
383
384 public function getHelpUrls() {
385 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allpages';
386 }
387}
const NS_MAIN
Definition Defines.php:64
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1454
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition ApiBase.php:1643
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:170
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition ApiBase.php:196
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:221
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:163
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:223
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
Query module to enumerate all available pages.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getCacheMode( $params)
Get the cache mode for the data generated by this module.
executeGenerator( $resultPageSet)
getHelpUrls()
Return links to more detailed help pages about the module.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getExamplesMessages()
Returns usage examples for this module.
__construct(ApiQuery $query, $moduleName, NamespaceInfo $namespaceInfo, GenderCache $genderCache, RestrictionStore $restrictionStore)
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.
addFields( $value)
Add a set of fields to select to the internal array.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
getDB()
Get the Query database connection (read-only)
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
addWhere( $value)
Add a set of WHERE clauses to the internal array.
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
This is the main query class.
Definition ApiQuery.php:41
Caches user genders when needed to use correct namespace aliases.
A class containing constants representing the names of configuration variables.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition Title.php:638
Service for formatting and validating API parameters.
Type definition for integer types.
return true
Definition router.php:92