MediaWiki master
ApiQueryAllPages.php
Go to the documentation of this file.
1<?php
9namespace MediaWiki\Api;
10
20
27
28 private NamespaceInfo $namespaceInfo;
29 private GenderCache $genderCache;
30 private RestrictionStore $restrictionStore;
31
32 public function __construct(
33 ApiQuery $query,
34 string $moduleName,
35 NamespaceInfo $namespaceInfo,
36 GenderCache $genderCache,
37 RestrictionStore $restrictionStore
38 ) {
39 parent::__construct( $query, $moduleName, 'ap' );
40 $this->namespaceInfo = $namespaceInfo;
41 $this->genderCache = $genderCache;
42 $this->restrictionStore = $restrictionStore;
43 }
44
45 public function execute() {
46 $this->run();
47 }
48
50 public function getCacheMode( $params ) {
51 return 'public';
52 }
53
58 public function executeGenerator( $resultPageSet ) {
59 if ( $resultPageSet->isResolvingRedirects() ) {
60 $this->dieWithError( 'apierror-allpages-generator-redirects', 'params' );
61 }
62
63 $this->run( $resultPageSet );
64 }
65
70 private function run( $resultPageSet = null ) {
71 $db = $this->getDB();
72
73 $params = $this->extractRequestParams();
74
75 // Page filters
76 $this->addTables( 'page' );
77
78 if ( $params['continue'] !== null ) {
79 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string' ] );
80 $op = $params['dir'] == 'descending' ? '<=' : '>=';
81 $this->addWhere( $db->expr( 'page_title', $op, $cont[0] ) );
82 }
83
84 $miserMode = $this->getConfig()->get( MainConfigNames::MiserMode );
85 if ( !$miserMode ) {
86 if ( $params['filterredir'] == 'redirects' ) {
87 $this->addWhereFld( 'page_is_redirect', 1 );
88 } elseif ( $params['filterredir'] == 'nonredirects' ) {
89 $this->addWhereFld( 'page_is_redirect', 0 );
90 }
91 }
92
93 $this->addWhereFld( 'page_namespace', $params['namespace'] );
94 $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
95 $from = ( $params['from'] === null
96 ? null
97 : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
98 $to = ( $params['to'] === null
99 ? null
100 : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
101 $this->addWhereRange( 'page_title', $dir, $from, $to );
102
103 if ( isset( $params['prefix'] ) ) {
104 $this->addWhere(
105 $db->expr(
106 'page_title',
107 IExpression::LIKE,
108 new LikeValue( $this->titlePartToKey( $params['prefix'], $params['namespace'] ), $db->anyString() )
109 )
110 );
111 }
112
113 if ( $resultPageSet === null ) {
114 $selectFields = [
115 'page_namespace',
116 'page_title',
117 'page_id'
118 ];
119 } else {
120 $selectFields = $resultPageSet->getPageTableFields();
121 }
122
123 $miserModeFilterRedirValue = null;
124 $miserModeFilterRedir = $miserMode && $params['filterredir'] !== 'all';
125 if ( $miserModeFilterRedir ) {
126 $selectFields[] = 'page_is_redirect';
127
128 if ( $params['filterredir'] == 'redirects' ) {
129 $miserModeFilterRedirValue = 1;
130 } elseif ( $params['filterredir'] == 'nonredirects' ) {
131 $miserModeFilterRedirValue = 0;
132 }
133 }
134
135 $this->addFields( $selectFields );
136 $forceNameTitleIndex = true;
137 if ( isset( $params['minsize'] ) ) {
138 $this->addWhere( 'page_len>=' . (int)$params['minsize'] );
139 $forceNameTitleIndex = false;
140 }
141
142 if ( !$miserMode && isset( $params['maxsize'] ) ) {
143 $this->addWhere( 'page_len<=' . (int)$params['maxsize'] );
144 $forceNameTitleIndex = false;
145 }
146
147 // Page protection filtering
148 if ( $params['prtype'] || $params['prexpiry'] != 'all' ) {
149 $this->addTables( 'page_restrictions' );
150 $this->addWhere( 'page_id=pr_page' );
151 $this->addWhere(
152 $db->expr( 'pr_expiry', '>', $db->timestamp() )->or( 'pr_expiry', '=', null )
153 );
154
155 if ( $params['prtype'] ) {
156 $this->addWhereFld( 'pr_type', $params['prtype'] );
157
158 if ( isset( $params['prlevel'] ) ) {
159 // Remove the empty string and '*' from the prlevel array
160 $prlevel = array_diff( $params['prlevel'], [ '', '*' ] );
161
162 if ( count( $prlevel ) ) {
163 $this->addWhereFld( 'pr_level', $prlevel );
164 }
165 }
166 if ( $params['prfiltercascade'] == 'cascading' ) {
167 $this->addWhereFld( 'pr_cascade', 1 );
168 } elseif ( $params['prfiltercascade'] == 'noncascading' ) {
169 $this->addWhereFld( 'pr_cascade', 0 );
170 }
171 }
172 $forceNameTitleIndex = false;
173
174 if ( $params['prexpiry'] == 'indefinite' ) {
175 $this->addWhereFld( 'pr_expiry', [ $db->getInfinity(), null ] );
176 } elseif ( $params['prexpiry'] == 'definite' ) {
177 $this->addWhere( $db->expr( 'pr_expiry', '!=', $db->getInfinity() ) );
178 }
179
180 $this->addOption( 'DISTINCT' );
181 } elseif ( isset( $params['prlevel'] ) ) {
182 $this->dieWithError(
183 [ 'apierror-invalidparammix-mustusewith', 'prlevel', 'prtype' ], 'invalidparammix'
184 );
185 }
186
187 if ( $params['filterlanglinks'] == 'withoutlanglinks' ) {
188 $this->addTables( 'langlinks' );
189 $this->addJoinConds( [ 'langlinks' => [ 'LEFT JOIN', 'page_id=ll_from' ] ] );
190 $this->addWhere( [ 'll_from' => null ] );
191 $forceNameTitleIndex = false;
192 } elseif ( $params['filterlanglinks'] == 'withlanglinks' ) {
193 $this->addTables( 'langlinks' );
194 $this->addWhere( 'page_id=ll_from' );
195 $this->addOption( 'STRAIGHT_JOIN' );
196
197 $dbType = $db->getType();
198 if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
199 // Avoid MySQL filesort reported in 2015 (T78276)
200 $this->addOption( 'GROUP BY', [ 'page_title' ] );
201 } else {
202 // SQL:1999 rules only counting primary keys
203 $this->addOption( 'GROUP BY', [ 'page_title', 'page_id' ] );
204 }
205
206 $forceNameTitleIndex = false;
207 }
208
209 if ( $forceNameTitleIndex ) {
210 $this->addOption( 'USE INDEX', 'page_name_title' );
211 }
212
213 $limit = $params['limit'];
214 $this->addOption( 'LIMIT', $limit + 1 );
215 $res = $this->select( __METHOD__ );
216
217 // Get gender information
218 if ( $this->namespaceInfo->hasGenderDistinction( $params['namespace'] ) ) {
219 $users = [];
220 foreach ( $res as $row ) {
221 $users[] = $row->page_title;
222 }
223 $this->genderCache->doQuery( $users, __METHOD__ );
224 $res->rewind(); // reset
225 }
226
227 $count = 0;
228 $result = $this->getResult();
229 foreach ( $res as $row ) {
230 if ( ++$count > $limit ) {
231 // We've reached the one extra which shows that there are
232 // additional pages to be had. Stop here...
233 $this->setContinueEnumParameter( 'continue', $row->page_title );
234 break;
235 }
236
237 if ( $miserModeFilterRedir && (int)$row->page_is_redirect !== $miserModeFilterRedirValue ) {
238 // Filter implemented in PHP due to being in Miser Mode
239 continue;
240 }
241
242 if ( $resultPageSet === null ) {
243 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
244 $vals = [
245 'pageid' => (int)$row->page_id,
246 'ns' => $title->getNamespace(),
247 'title' => $title->getPrefixedText()
248 ];
249 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
250 if ( !$fit ) {
251 $this->setContinueEnumParameter( 'continue', $row->page_title );
252 break;
253 }
254 } else {
255 $resultPageSet->processDbRow( $row );
256 }
257 }
258
259 if ( $resultPageSet === null ) {
260 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'p' );
261 }
262 }
263
265 public function getAllowedParams() {
266 $ret = [
267 'from' => null,
268 'continue' => [
269 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
270 ],
271 'to' => null,
272 'prefix' => null,
273 'namespace' => [
274 ParamValidator::PARAM_DEFAULT => NS_MAIN,
275 ParamValidator::PARAM_TYPE => 'namespace',
276 ],
277 'filterredir' => [
278 ParamValidator::PARAM_DEFAULT => 'all',
279 ParamValidator::PARAM_TYPE => [
280 'all',
281 'redirects',
282 'nonredirects'
283 ]
284 ],
285 'filterlanglinks' => [
286 ParamValidator::PARAM_TYPE => [
287 'withlanglinks',
288 'withoutlanglinks',
289 'all'
290 ],
291 ParamValidator::PARAM_DEFAULT => 'all'
292 ],
293 'minsize' => [
294 ParamValidator::PARAM_TYPE => 'integer',
295 ],
296 'maxsize' => [
297 ParamValidator::PARAM_TYPE => 'integer',
298 ],
299 'prtype' => [
300 ParamValidator::PARAM_TYPE => $this->restrictionStore->listAllRestrictionTypes( true ),
301 ParamValidator::PARAM_ISMULTI => true
302 ],
303 'prlevel' => [
304 ParamValidator::PARAM_TYPE =>
306 ParamValidator::PARAM_ISMULTI => true
307 ],
308 'prfiltercascade' => [
309 ParamValidator::PARAM_DEFAULT => 'all',
310 ParamValidator::PARAM_TYPE => [
311 'cascading',
312 'noncascading',
313 'all'
314 ],
315 ],
316 'prexpiry' => [
317 ParamValidator::PARAM_TYPE => [
318 'indefinite',
319 'definite',
320 'all'
321 ],
322 ParamValidator::PARAM_DEFAULT => 'all',
324 ],
325 'limit' => [
326 ParamValidator::PARAM_DEFAULT => 10,
327 ParamValidator::PARAM_TYPE => 'limit',
328 IntegerDef::PARAM_MIN => 1,
329 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
330 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
331 ],
332 'dir' => [
333 ParamValidator::PARAM_DEFAULT => 'ascending',
334 ParamValidator::PARAM_TYPE => [
335 'ascending',
336 'descending'
337 ]
338 ],
339 ];
340
341 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
342 $ret['filterredir'][ApiBase::PARAM_HELP_MSG_APPEND] = [ 'api-help-param-limited-in-miser-mode' ];
343 $ret['maxsize'][ApiBase::PARAM_HELP_MSG_APPEND] = [ 'api-help-param-disabled-in-miser-mode' ];
344 }
345
346 return $ret;
347 }
348
350 protected function getExamplesMessages() {
351 return [
352 'action=query&list=allpages&apfrom=B'
353 => 'apihelp-query+allpages-example-b',
354 'action=query&generator=allpages&gaplimit=4&gapfrom=T&prop=info'
355 => 'apihelp-query+allpages-example-generator',
356 'action=query&generator=allpages&gaplimit=2&' .
357 'gapfilterredir=nonredirects&gapfrom=Re&prop=revisions&rvprop=content'
358 => 'apihelp-query+allpages-example-generator-revisions',
359 ];
360 }
361
363 public function getHelpUrls() {
364 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allpages';
365 }
366}
367
369class_alias( ApiQueryAllPages::class, 'ApiQueryAllPages' );
const NS_MAIN
Definition Defines.php:51
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1511
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:543
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1696
getResult()
Get the result object.
Definition ApiBase.php:682
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:207
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition ApiBase.php:175
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:167
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:234
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:823
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:232
Query module to enumerate all available pages.
getCacheMode( $params)
Get the cache mode for the data generated by this module.Override this in the module subclass....
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
__construct(ApiQuery $query, string $moduleName, NamespaceInfo $namespaceInfo, GenderCache $genderCache, RestrictionStore $restrictionStore)
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.
addJoinConds( $join_conds)
Add a set of JOIN conditions 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.
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.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addFields( $value)
Add a set of fields to select to the internal array.
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.
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
This is the main query class.
Definition ApiQuery.php:36
Look up "gender" user preference.
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
A class containing constants representing the names of configuration variables.
const RestrictionLevels
Name constant for the RestrictionLevels setting, for use with Config::get()
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
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:69
Service for formatting and validating API parameters.
Type definition for integer types.
Content of like value.
Definition LikeValue.php:14
array $params
The job parameters.