MediaWiki master
ApiQueryAllPages.php
Go to the documentation of this file.
1<?php
32
39
40 private NamespaceInfo $namespaceInfo;
41 private GenderCache $genderCache;
42 private RestrictionStore $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
92
93 // Page filters
94 $this->addTables( 'page' );
95
96 if ( $params['continue'] !== null ) {
97 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string' ] );
98 $op = $params['dir'] == 'descending' ? '<=' : '>=';
99 $this->addWhere( $db->expr( 'page_title', $op, $cont[0] ) );
100 }
101
102 $miserMode = $this->getConfig()->get( MainConfigNames::MiserMode );
103 if ( !$miserMode ) {
104 if ( $params['filterredir'] == 'redirects' ) {
105 $this->addWhereFld( 'page_is_redirect', 1 );
106 } elseif ( $params['filterredir'] == 'nonredirects' ) {
107 $this->addWhereFld( 'page_is_redirect', 0 );
108 }
109 }
110
111 $this->addWhereFld( 'page_namespace', $params['namespace'] );
112 $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
113 $from = ( $params['from'] === null
114 ? null
115 : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
116 $to = ( $params['to'] === null
117 ? null
118 : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
119 $this->addWhereRange( 'page_title', $dir, $from, $to );
120
121 if ( isset( $params['prefix'] ) ) {
122 $this->addWhere(
123 $db->expr(
124 'page_title',
125 IExpression::LIKE,
126 new LikeValue( $this->titlePartToKey( $params['prefix'], $params['namespace'] ), $db->anyString() )
127 )
128 );
129 }
130
131 if ( $resultPageSet === null ) {
132 $selectFields = [
133 'page_namespace',
134 'page_title',
135 'page_id'
136 ];
137 } else {
138 $selectFields = $resultPageSet->getPageTableFields();
139 }
140
141 $miserModeFilterRedirValue = null;
142 $miserModeFilterRedir = $miserMode && $params['filterredir'] !== 'all';
143 if ( $miserModeFilterRedir ) {
144 $selectFields[] = 'page_is_redirect';
145
146 if ( $params['filterredir'] == 'redirects' ) {
147 $miserModeFilterRedirValue = 1;
148 } elseif ( $params['filterredir'] == 'nonredirects' ) {
149 $miserModeFilterRedirValue = 0;
150 }
151 }
152
153 $this->addFields( $selectFields );
154 $forceNameTitleIndex = true;
155 if ( isset( $params['minsize'] ) ) {
156 $this->addWhere( 'page_len>=' . (int)$params['minsize'] );
157 $forceNameTitleIndex = false;
158 }
159
160 if ( isset( $params['maxsize'] ) ) {
161 $this->addWhere( 'page_len<=' . (int)$params['maxsize'] );
162 $forceNameTitleIndex = false;
163 }
164
165 // Page protection filtering
166 if ( $params['prtype'] || $params['prexpiry'] != 'all' ) {
167 $this->addTables( 'page_restrictions' );
168 $this->addWhere( 'page_id=pr_page' );
169 $this->addWhere(
170 $db->expr( 'pr_expiry', '>', $db->timestamp() )->or( 'pr_expiry', '=', null )
171 );
172
173 if ( $params['prtype'] ) {
174 $this->addWhereFld( 'pr_type', $params['prtype'] );
175
176 if ( isset( $params['prlevel'] ) ) {
177 // Remove the empty string and '*' from the prlevel array
178 $prlevel = array_diff( $params['prlevel'], [ '', '*' ] );
179
180 if ( count( $prlevel ) ) {
181 $this->addWhereFld( 'pr_level', $prlevel );
182 }
183 }
184 if ( $params['prfiltercascade'] == 'cascading' ) {
185 $this->addWhereFld( 'pr_cascade', 1 );
186 } elseif ( $params['prfiltercascade'] == 'noncascading' ) {
187 $this->addWhereFld( 'pr_cascade', 0 );
188 }
189 }
190 $forceNameTitleIndex = false;
191
192 if ( $params['prexpiry'] == 'indefinite' ) {
193 $this->addWhereFld( 'pr_expiry', [ $db->getInfinity(), null ] );
194 } elseif ( $params['prexpiry'] == 'definite' ) {
195 $this->addWhere( $db->expr( 'pr_expiry', '!=', $db->getInfinity() ) );
196 }
197
198 $this->addOption( 'DISTINCT' );
199 } elseif ( isset( $params['prlevel'] ) ) {
200 $this->dieWithError(
201 [ 'apierror-invalidparammix-mustusewith', 'prlevel', 'prtype' ], 'invalidparammix'
202 );
203 }
204
205 if ( $params['filterlanglinks'] == 'withoutlanglinks' ) {
206 $this->addTables( 'langlinks' );
207 $this->addJoinConds( [ 'langlinks' => [ 'LEFT JOIN', 'page_id=ll_from' ] ] );
208 $this->addWhere( [ 'll_from' => null ] );
209 $forceNameTitleIndex = false;
210 } elseif ( $params['filterlanglinks'] == 'withlanglinks' ) {
211 $this->addTables( 'langlinks' );
212 $this->addWhere( 'page_id=ll_from' );
213 $this->addOption( 'STRAIGHT_JOIN' );
214
215 // MySQL filesorts if we use a GROUP BY that works with the rules
216 // in the 1992 SQL standard (it doesn't like having the
217 // constant-in-WHERE page_namespace column in there). Using the
218 // 1999 rules works fine, but that breaks other DBs. Sigh.
219 // @todo Once we drop support for 1992-rule DBs, we can simplify this.
220 $dbType = $db->getType();
221 if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
222 // Ignore the rules, or 1999 rules if you count unique keys
223 // over non-NULL columns as satisfying the requirement for
224 // "functional dependency" and don't require including
225 // constant-in-WHERE columns in the GROUP BY.
226 $this->addOption( 'GROUP BY', [ 'page_title' ] );
227 } elseif ( $dbType === 'postgres' && $db->getServerVersion() >= 9.1 ) {
228 // 1999 rules only counting primary keys
229 $this->addOption( 'GROUP BY', [ 'page_title', 'page_id' ] );
230 } else {
231 // 1992 rules
232 $this->addOption( 'GROUP BY', $selectFields );
233 }
234
235 $forceNameTitleIndex = false;
236 }
237
238 if ( $forceNameTitleIndex ) {
239 $this->addOption( 'USE INDEX', 'page_name_title' );
240 }
241
242 $limit = $params['limit'];
243 $this->addOption( 'LIMIT', $limit + 1 );
244 $res = $this->select( __METHOD__ );
245
246 // Get gender information
247 if ( $this->namespaceInfo->hasGenderDistinction( $params['namespace'] ) ) {
248 $users = [];
249 foreach ( $res as $row ) {
250 $users[] = $row->page_title;
251 }
252 $this->genderCache->doQuery( $users, __METHOD__ );
253 $res->rewind(); // reset
254 }
255
256 $count = 0;
257 $result = $this->getResult();
258 foreach ( $res as $row ) {
259 if ( ++$count > $limit ) {
260 // We've reached the one extra which shows that there are
261 // additional pages to be had. Stop here...
262 $this->setContinueEnumParameter( 'continue', $row->page_title );
263 break;
264 }
265
266 if ( $miserModeFilterRedir && (int)$row->page_is_redirect !== $miserModeFilterRedirValue ) {
267 // Filter implemented in PHP due to being in Miser Mode
268 continue;
269 }
270
271 if ( $resultPageSet === null ) {
272 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
273 $vals = [
274 'pageid' => (int)$row->page_id,
275 'ns' => $title->getNamespace(),
276 'title' => $title->getPrefixedText()
277 ];
278 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
279 if ( !$fit ) {
280 $this->setContinueEnumParameter( 'continue', $row->page_title );
281 break;
282 }
283 } else {
284 $resultPageSet->processDbRow( $row );
285 }
286 }
287
288 if ( $resultPageSet === null ) {
289 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'p' );
290 }
291 }
292
293 public function getAllowedParams() {
294 $ret = [
295 'from' => null,
296 'continue' => [
297 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
298 ],
299 'to' => null,
300 'prefix' => null,
301 'namespace' => [
302 ParamValidator::PARAM_DEFAULT => NS_MAIN,
303 ParamValidator::PARAM_TYPE => 'namespace',
304 ],
305 'filterredir' => [
306 ParamValidator::PARAM_DEFAULT => 'all',
307 ParamValidator::PARAM_TYPE => [
308 'all',
309 'redirects',
310 'nonredirects'
311 ]
312 ],
313 'filterlanglinks' => [
314 ParamValidator::PARAM_TYPE => [
315 'withlanglinks',
316 'withoutlanglinks',
317 'all'
318 ],
319 ParamValidator::PARAM_DEFAULT => 'all'
320 ],
321 'minsize' => [
322 ParamValidator::PARAM_TYPE => 'integer',
323 ],
324 'maxsize' => [
325 ParamValidator::PARAM_TYPE => 'integer',
326 ],
327 'prtype' => [
328 ParamValidator::PARAM_TYPE => $this->restrictionStore->listAllRestrictionTypes( true ),
329 ParamValidator::PARAM_ISMULTI => true
330 ],
331 'prlevel' => [
332 ParamValidator::PARAM_TYPE =>
333 $this->getConfig()->get( MainConfigNames::RestrictionLevels ),
334 ParamValidator::PARAM_ISMULTI => true
335 ],
336 'prfiltercascade' => [
337 ParamValidator::PARAM_DEFAULT => 'all',
338 ParamValidator::PARAM_TYPE => [
339 'cascading',
340 'noncascading',
341 'all'
342 ],
343 ],
344 'prexpiry' => [
345 ParamValidator::PARAM_TYPE => [
346 'indefinite',
347 'definite',
348 'all'
349 ],
350 ParamValidator::PARAM_DEFAULT => 'all',
352 ],
353 'limit' => [
354 ParamValidator::PARAM_DEFAULT => 10,
355 ParamValidator::PARAM_TYPE => 'limit',
356 IntegerDef::PARAM_MIN => 1,
357 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
358 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
359 ],
360 'dir' => [
361 ParamValidator::PARAM_DEFAULT => 'ascending',
362 ParamValidator::PARAM_TYPE => [
363 'ascending',
364 'descending'
365 ]
366 ],
367 ];
368
369 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
370 $ret['filterredir'][ApiBase::PARAM_HELP_MSG_APPEND] = [ 'api-help-param-limited-in-miser-mode' ];
371 }
372
373 return $ret;
374 }
375
376 protected function getExamplesMessages() {
377 return [
378 'action=query&list=allpages&apfrom=B'
379 => 'apihelp-query+allpages-example-b',
380 'action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info'
381 => 'apihelp-query+allpages-example-generator',
382 'action=query&generator=allpages&gaplimit=2&' .
383 'gapfilterredir=nonredirects&gapfrom=Re&prop=revisions&rvprop=content'
384 => 'apihelp-query+allpages-example-generator-revisions',
385 ];
386 }
387
388 public function getHelpUrls() {
389 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allpages';
390 }
391}
const NS_MAIN
Definition Defines.php:64
array $params
The job parameters.
run()
Run the job.
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1542
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:178
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1734
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:211
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:236
getResult()
Get the result object.
Definition ApiBase.php:680
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:820
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:171
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:238
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:541
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:43
Look up "gender" user preference.
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...
Represents a title within MediaWiki.
Definition Title.php:78
Service for formatting and validating API parameters.
Type definition for integer types.
Content of like value.
Definition LikeValue.php:14