MediaWiki REL1_39
LinkBatch.php
Go to the documentation of this file.
1<?php
32use Psr\Log\LoggerInterface;
33use Wikimedia\Assert\Assert;
37
44class LinkBatch {
48 public $data = [];
49
53 private $pageIdentities = null;
54
58 protected $caller;
59
63 private $linkCache;
64
68 private $titleFormatter;
69
73 private $contentLanguage;
74
78 private $genderCache;
79
83 private $loadBalancer;
84
86 private $linksMigration;
87
89 private $logger;
90
102 public function __construct(
103 iterable $arr = [],
104 ?LinkCache $linkCache = null,
105 ?TitleFormatter $titleFormatter = null,
106 ?Language $contentLanguage = null,
107 ?GenderCache $genderCache = null,
108 ?ILoadBalancer $loadBalancer = null,
109 ?LinksMigration $linksMigration = null,
110 ?LoggerInterface $logger = null
111 ) {
112 $getServices = static function () {
113 // BC hack. Use a closure so this can be unit-tested.
114 return MediaWikiServices::getInstance();
115 };
116
117 $this->linkCache = $linkCache ?? $getServices()->getLinkCache();
118 $this->titleFormatter = $titleFormatter ?? $getServices()->getTitleFormatter();
119 $this->contentLanguage = $contentLanguage ?? $getServices()->getContentLanguage();
120 $this->genderCache = $genderCache ?? $getServices()->getGenderCache();
121 $this->loadBalancer = $loadBalancer ?? $getServices()->getDBLoadBalancer();
122 $this->linksMigration = $linksMigration ?? $getServices()->getLinksMigration();
123 $this->logger = $logger ?? LoggerFactory::getInstance( 'LinkBatch' );
124
125 foreach ( $arr as $item ) {
126 $this->addObj( $item );
127 }
128 }
129
138 public function setCaller( $caller ) {
139 $this->caller = $caller;
140
141 return $this;
142 }
143
147 public function addObj( $link ) {
148 if ( !$link ) {
149 // Don't die if we got null, just skip. There is nothing to do anyway.
150 // For now, let's avoid things like T282180. We should be more strict in the future.
151 $this->logger->warning(
152 'Skipping null link, probably due to a bad title.',
153 [ 'exception' => new RuntimeException() ]
154 );
155 return;
156 }
157 if ( $link instanceof LinkTarget && $link->isExternal() ) {
158 $this->logger->warning(
159 'Skipping interwiki link',
160 [ 'exception' => new RuntimeException() ]
161 );
162 return;
163 }
164
165 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $link, '$link' );
166 $this->add( $link->getNamespace(), $link->getDBkey() );
167 }
168
173 public function add( $ns, $dbkey ) {
174 if ( $ns < 0 || $dbkey === '' ) {
175 // T137083
176 return;
177 }
178 if ( !array_key_exists( $ns, $this->data ) ) {
179 $this->data[$ns] = [];
180 }
181
182 $this->data[$ns][strtr( $dbkey, ' ', '_' )] = 1;
183 }
184
191 public function setArray( $array ) {
192 $this->data = $array;
193 }
194
200 public function isEmpty() {
201 return $this->getSize() == 0;
202 }
203
209 public function getSize() {
210 return count( $this->data );
211 }
212
218 public function execute() {
219 return $this->executeInto( $this->linkCache );
220 }
221
229 public function getPageIdentities(): array {
230 if ( $this->pageIdentities === null ) {
231 $this->execute();
232 }
233
234 return $this->pageIdentities;
235 }
236
244 protected function executeInto( $cache ) {
245 $res = $this->doQuery();
246 $this->doGenderQuery();
247 return $this->addResultToCache( $cache, $res );
248 }
249
260 public function addResultToCache( $cache, $res ) {
261 if ( !$res ) {
262 return [];
263 }
264
265 // For each returned entry, add it to the list of good links, and remove it from $remaining
266
267 if ( $this->pageIdentities === null ) {
268 $this->pageIdentities = [];
269 }
270
271 $ids = [];
272 $remaining = $this->data;
273 foreach ( $res as $row ) {
274 try {
275 $title = new TitleValue( (int)$row->page_namespace, $row->page_title );
276
277 $cache->addGoodLinkObjFromRow( $title, $row );
278 $pdbk = $this->titleFormatter->getPrefixedDBkey( $title );
279 $ids[$pdbk] = $row->page_id;
280
281 $pageIdentity = new PageIdentityValue(
282 (int)$row->page_id,
283 (int)$row->page_namespace,
284 $row->page_title,
285 ProperPageIdentity::LOCAL
286 );
287
288 $key = CacheKeyHelper::getKeyForPage( $pageIdentity );
289 $this->pageIdentities[$key] = $pageIdentity;
290 } catch ( InvalidArgumentException $ex ) {
291 $this->logger->warning(
292 'Encountered invalid title',
293 [ 'title_namespace' => $row->page_namespace, 'title_dbkey' => $row->page_title ]
294 );
295 }
296
297 unset( $remaining[$row->page_namespace][$row->page_title] );
298 }
299
300 // The remaining links in $data are bad links, register them as such
301 foreach ( $remaining as $ns => $dbkeys ) {
302 foreach ( $dbkeys as $dbkey => $unused ) {
303 try {
304 $title = new TitleValue( (int)$ns, (string)$dbkey );
305
306 $cache->addBadLinkObj( $title );
307 $pdbk = $this->titleFormatter->getPrefixedDBkey( $title );
308 $ids[$pdbk] = 0;
309
310 $pageIdentity = new PageIdentityValue( 0, (int)$ns, $dbkey, ProperPageIdentity::LOCAL );
311 $key = CacheKeyHelper::getKeyForPage( $pageIdentity );
312 $this->pageIdentities[$key] = $pageIdentity;
313 } catch ( InvalidArgumentException $ex ) {
314 $this->logger->warning(
315 'Encountered invalid title',
316 [ 'title_namespace' => $ns, 'title_dbkey' => $dbkey ]
317 );
318 }
319 }
320 }
321
322 return $ids;
323 }
324
329 public function doQuery() {
330 if ( $this->isEmpty() ) {
331 return false;
332 }
333
334 // This is similar to LinkHolderArray::replaceInternal
335 $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
336 $table = 'page';
337 $fields = LinkCache::getSelectFields();
338
339 $conds = $this->constructSet( 'page', $dbr );
340
341 // Do query
342 $caller = __METHOD__;
343 if ( strval( $this->caller ) !== '' ) {
344 $caller .= " (for {$this->caller})";
345 }
346
347 return $dbr->select( $table, $fields, $conds, $caller );
348 }
349
355 public function doGenderQuery() {
356 if ( $this->isEmpty() ) {
357 return false;
358 }
359
360 if ( !$this->contentLanguage->needsGenderDistinction() ) {
361 return false;
362 }
363
364 $this->genderCache->doLinkBatch( $this->data, $this->caller );
365
366 return true;
367 }
368
376 public function constructSet( $prefix, $db ) {
377 if ( isset( $this->linksMigration::$prefixToTableMapping[$prefix] ) ) {
378 list( $blNamespace, $blTitle ) = $this->linksMigration->getTitleFields(
379 $this->linksMigration::$prefixToTableMapping[$prefix]
380 );
381 } else {
382 $blNamespace = "{$prefix}_namespace";
383 $blTitle = "{$prefix}_title";
384 }
385 return $db->makeWhereFrom2d( $this->data, $blNamespace, $blTitle );
386 }
387}
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
Caches user genders when needed to use correct namespace aliases.
Base class for language-specific code.
Definition Language.php:53
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition LinkBatch.php:44
doGenderQuery()
Do (and cache) {{GENDER:...}} information for userpages in this LinkBatch.
add( $ns, $dbkey)
getSize()
Returns the size of the batch.
__construct(iterable $arr=[], ?LinkCache $linkCache=null, ?TitleFormatter $titleFormatter=null, ?Language $contentLanguage=null, ?GenderCache $genderCache=null, ?ILoadBalancer $loadBalancer=null, ?LinksMigration $linksMigration=null, ?LoggerInterface $logger=null)
addObj( $link)
addResultToCache( $cache, $res)
Add a result wrapper containing IDs and titles to a LinkCache object.
isEmpty()
Returns true if no pages have been added, false otherwise.
setCaller( $caller)
Use ->setCaller( METHOD ) to indicate which code is using this class.
constructSet( $prefix, $db)
Construct a WHERE clause which will match all the given titles.
getPageIdentities()
Do the query, add the results to the LinkCache object, and return ProperPageIdentity instances corres...
execute()
Do the query and add the results to the LinkCache object.
setArray( $array)
Set the link list to a given 2-d array First key is the namespace, second is the DB key,...
string null $caller
For debugging which method is using this class.
Definition LinkBatch.php:58
array[] $data
2-d array, first index namespace, second index dbkey, value arbitrary
Definition LinkBatch.php:48
executeInto( $cache)
Do the query and add the results to a given LinkCache object Return an array mapping PDBK to ID.
doQuery()
Perform the existence test query, return a result wrapper with page_id fields.
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition LinkCache.php:42
Helper class for mapping value objects representing basic entities to cache keys.
Service for compat reading of links tables.
PSR-3 logger instance factory.
Service locator for MediaWiki core services.
Immutable value object representing a page identity.
Represents a page (or page fragment) title within MediaWiki.
isExternal()
Whether this LinkTarget has an interwiki component.
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Interface for a page that is (or could be, or used to be) an editable wiki page.
A title formatter service for MediaWiki.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
Create and track the database connections and transactions for a given database cluster.
Result wrapper for grabbing data queried from an IDatabase object.
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:26