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