MediaWiki 1.40.4
ApiQueryAllLinks.php
Go to the documentation of this file.
1<?php
28
35
36 private $table, $tablePrefix, $indexTag;
37 private $fieldTitle = 'title';
38 private $dfltNamespace = NS_MAIN;
39 private $hasNamespace = true;
40 private $useIndex = null;
41 private $props = [];
42
44 private $namespaceInfo;
45
47 private $genderCache;
48
50 private $linksMigration;
51
59 public function __construct(
60 ApiQuery $query,
61 $moduleName,
62 NamespaceInfo $namespaceInfo,
63 GenderCache $genderCache,
64 LinksMigration $linksMigration
65 ) {
66 switch ( $moduleName ) {
67 case 'alllinks':
68 $prefix = 'al';
69 $this->table = 'pagelinks';
70 $this->tablePrefix = 'pl_';
71 $this->useIndex = 'pl_namespace';
72 $this->indexTag = 'l';
73 break;
74 case 'alltransclusions':
75 $prefix = 'at';
76 $this->table = 'templatelinks';
77 $this->tablePrefix = 'tl_';
78 $this->dfltNamespace = NS_TEMPLATE;
79 $this->useIndex = 'tl_namespace';
80 $this->indexTag = 't';
81 break;
82 case 'allfileusages':
83 $prefix = 'af';
84 $this->table = 'imagelinks';
85 $this->tablePrefix = 'il_';
86 $this->fieldTitle = 'to';
87 $this->dfltNamespace = NS_FILE;
88 $this->hasNamespace = false;
89 $this->indexTag = 'f';
90 break;
91 case 'allredirects':
92 $prefix = 'ar';
93 $this->table = 'redirect';
94 $this->tablePrefix = 'rd_';
95 $this->indexTag = 'r';
96 $this->props = [
97 'fragment' => 'rd_fragment',
98 'interwiki' => 'rd_interwiki',
99 ];
100 break;
101 default:
102 ApiBase::dieDebug( __METHOD__, 'Unknown module name' );
103 }
104
105 parent::__construct( $query, $moduleName, $prefix );
106 $this->namespaceInfo = $namespaceInfo;
107 $this->genderCache = $genderCache;
108 $this->linksMigration = $linksMigration;
109 }
110
111 public function execute() {
112 $this->run();
113 }
114
115 public function getCacheMode( $params ) {
116 return 'public';
117 }
118
119 public function executeGenerator( $resultPageSet ) {
120 $this->run( $resultPageSet );
121 }
122
127 private function run( $resultPageSet = null ) {
128 $db = $this->getDB();
129 $params = $this->extractRequestParams();
130
131 $pfx = $this->tablePrefix;
132
133 $nsField = $pfx . 'namespace';
134 $titleField = $pfx . $this->fieldTitle;
135 if ( isset( $this->linksMigration::$mapping[$this->table] ) ) {
136 [ $nsField, $titleField ] = $this->linksMigration->getTitleFields( $this->table );
137 $queryInfo = $this->linksMigration->getQueryInfo( $this->table );
138 $this->addTables( $queryInfo['tables'] );
139 $this->addJoinConds( $queryInfo['joins'] );
140 } else {
141 if ( $this->useIndex ) {
142 $this->addOption( 'USE INDEX', $this->useIndex );
143 }
144 $this->addTables( $this->table );
145 }
146
147 $prop = array_fill_keys( $params['prop'], true );
148 $fld_ids = isset( $prop['ids'] );
149 $fld_title = isset( $prop['title'] );
150 if ( $this->hasNamespace ) {
151 $namespace = $params['namespace'];
152 } else {
153 $namespace = $this->dfltNamespace;
154 }
155
156 if ( $params['unique'] ) {
157 $matches = array_intersect_key( $prop, $this->props + [ 'ids' => 1 ] );
158 if ( $matches ) {
159 $p = $this->getModulePrefix();
160 $this->dieWithError(
161 [
162 'apierror-invalidparammix-cannotusewith',
163 "{$p}prop=" . implode( '|', array_keys( $matches ) ),
164 "{$p}unique"
165 ],
166 'invalidparammix'
167 );
168 }
169 $this->addOption( 'DISTINCT' );
170 }
171
172 if ( $this->hasNamespace ) {
173 $this->addWhereFld( $nsField, $namespace );
174 }
175
176 $continue = $params['continue'] !== null;
177 if ( $continue ) {
178 $op = $params['dir'] == 'descending' ? '<=' : '>=';
179 if ( $params['unique'] ) {
180 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string' ] );
181 $this->addWhere( $db->buildComparison( $op, [ $titleField => $cont[0] ] ) );
182 } else {
183 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string', 'int' ] );
184 $this->addWhere( $db->buildComparison( $op, [
185 $titleField => $cont[0],
186 "{$pfx}from" => $cont[1],
187 ] ) );
188 }
189 }
190
191 // 'continue' always overrides 'from'
192 $from = $continue || $params['from'] === null ? null :
193 $this->titlePartToKey( $params['from'], $namespace );
194 $to = $params['to'] === null ? null :
195 $this->titlePartToKey( $params['to'], $namespace );
196 $this->addWhereRange( $titleField, 'newer', $from, $to );
197
198 if ( isset( $params['prefix'] ) ) {
199 $this->addWhere( $titleField . $db->buildLike( $this->titlePartToKey(
200 $params['prefix'], $namespace ), $db->anyString() ) );
201 }
202
203 $this->addFields( [ 'pl_title' => $titleField ] );
204 $this->addFieldsIf( [ 'pl_from' => $pfx . 'from' ], !$params['unique'] );
205 foreach ( $this->props as $name => $field ) {
206 $this->addFieldsIf( $field, isset( $prop[$name] ) );
207 }
208
209 $limit = $params['limit'];
210 $this->addOption( 'LIMIT', $limit + 1 );
211
212 $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
213 $orderBy = [];
214 $orderBy[] = $titleField . $sort;
215 if ( !$params['unique'] ) {
216 $orderBy[] = $pfx . 'from' . $sort;
217 }
218 $this->addOption( 'ORDER BY', $orderBy );
219
220 $res = $this->select( __METHOD__ );
221
222 // Get gender information
223 if ( $resultPageSet === null && $res->numRows() && $this->namespaceInfo->hasGenderDistinction( $namespace ) ) {
224 $users = [];
225 foreach ( $res as $row ) {
226 $users[] = $row->pl_title;
227 }
228 if ( $users !== [] ) {
229 $this->genderCache->doQuery( $users, __METHOD__ );
230 }
231 }
232
233 $pageids = [];
234 $titles = [];
235 $count = 0;
236 $result = $this->getResult();
237 foreach ( $res as $row ) {
238 if ( ++$count > $limit ) {
239 // We've reached the one extra which shows that there are
240 // additional pages to be had. Stop here...
241 if ( $params['unique'] ) {
242 $this->setContinueEnumParameter( 'continue', $row->pl_title );
243 } else {
244 $this->setContinueEnumParameter( 'continue', $row->pl_title . '|' . $row->pl_from );
245 }
246 break;
247 }
248
249 if ( $resultPageSet === null ) {
250 $vals = [
251 ApiResult::META_TYPE => 'assoc',
252 ];
253 if ( $fld_ids ) {
254 $vals['fromid'] = (int)$row->pl_from;
255 }
256 if ( $fld_title ) {
257 $title = Title::makeTitle( $namespace, $row->pl_title );
259 }
260 foreach ( $this->props as $name => $field ) {
261 if ( isset( $prop[$name] ) && $row->$field !== null && $row->$field !== '' ) {
262 $vals[$name] = $row->$field;
263 }
264 }
265 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
266 if ( !$fit ) {
267 if ( $params['unique'] ) {
268 $this->setContinueEnumParameter( 'continue', $row->pl_title );
269 } else {
270 $this->setContinueEnumParameter( 'continue', $row->pl_title . '|' . $row->pl_from );
271 }
272 break;
273 }
274 } elseif ( $params['unique'] ) {
275 $titles[] = Title::makeTitle( $namespace, $row->pl_title );
276 } else {
277 $pageids[] = $row->pl_from;
278 }
279 }
280
281 if ( $resultPageSet === null ) {
282 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], $this->indexTag );
283 } elseif ( $params['unique'] ) {
284 $resultPageSet->populateFromTitles( $titles );
285 } else {
286 $resultPageSet->populateFromPageIDs( $pageids );
287 }
288 }
289
290 public function getAllowedParams() {
291 $allowedParams = [
292 'continue' => [
293 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
294 ],
295 'from' => null,
296 'to' => null,
297 'prefix' => null,
298 'unique' => false,
299 'prop' => [
300 ParamValidator::PARAM_ISMULTI => true,
301 ParamValidator::PARAM_DEFAULT => 'title',
302 ParamValidator::PARAM_TYPE => array_merge(
303 [ 'ids', 'title' ], array_keys( $this->props )
304 ),
306 ],
307 'namespace' => [
308 ParamValidator::PARAM_DEFAULT => $this->dfltNamespace,
309 ParamValidator::PARAM_TYPE => 'namespace',
310 NamespaceDef::PARAM_EXTRA_NAMESPACES => [ NS_MEDIA, NS_SPECIAL ],
311 ],
312 'limit' => [
313 ParamValidator::PARAM_DEFAULT => 10,
314 ParamValidator::PARAM_TYPE => 'limit',
315 IntegerDef::PARAM_MIN => 1,
316 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
317 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
318 ],
319 'dir' => [
320 ParamValidator::PARAM_DEFAULT => 'ascending',
321 ParamValidator::PARAM_TYPE => [
322 'ascending',
323 'descending'
324 ]
325 ],
326 ];
327 if ( !$this->hasNamespace ) {
328 unset( $allowedParams['namespace'] );
329 }
330
331 return $allowedParams;
332 }
333
334 protected function getExamplesMessages() {
335 $p = $this->getModulePrefix();
336 $name = $this->getModuleName();
337 $path = $this->getModulePath();
338
339 return [
340 "action=query&list={$name}&{$p}from=B&{$p}prop=ids|title"
341 => "apihelp-$path-example-b",
342 "action=query&list={$name}&{$p}unique=&{$p}from=B"
343 => "apihelp-$path-example-unique",
344 "action=query&generator={$name}&g{$p}unique=&g{$p}from=B"
345 => "apihelp-$path-example-unique-generator",
346 "action=query&generator={$name}&g{$p}from=B"
347 => "apihelp-$path-example-generator",
348 ];
349 }
350
351 public function getHelpUrls() {
352 $name = ucfirst( $this->getModuleName() );
353
354 return "https://www.mediawiki.org/wiki/Special:MyLanguage/API:{$name}";
355 }
356}
const NS_FILE
Definition Defines.php:70
const NS_MAIN
Definition Defines.php:64
const NS_TEMPLATE
Definition Defines.php:74
const NS_SPECIAL
Definition Defines.php:53
const NS_MEDIA
Definition Defines.php:52
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1460
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition ApiBase.php:514
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition ApiBase.php:1701
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1649
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:204
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:229
getResult()
Get the result object.
Definition ApiBase.php:637
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:773
getModulePath()
Get the path to this module.
Definition ApiBase.php:581
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:231
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:506
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result 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.
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.
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
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:42
const META_TYPE
Key for the 'type' metadata item.
Caches user genders when needed to use correct namespace aliases.
Service for compat reading of links tables.
Type definition for namespace types.
Represents a title within MediaWiki.
Definition Title.php:82
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Service for formatting and validating API parameters.
Type definition for integer types.