MediaWiki  master
LinkBatch.php
Go to the documentation of this file.
1 <?php
32 use Psr\Log\LoggerInterface;
33 use Wikimedia\Assert\Assert;
37 
44 class 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  [ $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.
Definition: GenderCache.php:34
Base class for language-specific code.
Definition: Language.php:54
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.
Definition: LinkBatch.php:355
add( $ns, $dbkey)
Definition: LinkBatch.php:173
getSize()
Returns the size of the batch.
Definition: LinkBatch.php:209
__construct(iterable $arr=[], ?LinkCache $linkCache=null, ?TitleFormatter $titleFormatter=null, ?Language $contentLanguage=null, ?GenderCache $genderCache=null, ?ILoadBalancer $loadBalancer=null, ?LinksMigration $linksMigration=null, ?LoggerInterface $logger=null)
Definition: LinkBatch.php:102
addObj( $link)
Definition: LinkBatch.php:147
addResultToCache( $cache, $res)
Add a result wrapper containing IDs and titles to a LinkCache object.
Definition: LinkBatch.php:260
isEmpty()
Returns true if no pages have been added, false otherwise.
Definition: LinkBatch.php:200
setCaller( $caller)
Use ->setCaller( METHOD ) to indicate which code is using this class.
Definition: LinkBatch.php:138
constructSet( $prefix, $db)
Construct a WHERE clause which will match all the given titles.
Definition: LinkBatch.php:376
getPageIdentities()
Do the query, add the results to the LinkCache object, and return ProperPageIdentity instances corres...
Definition: LinkBatch.php:229
execute()
Do the query and add the results to the LinkCache object.
Definition: LinkBatch.php:218
setArray( $array)
Set the link list to a given 2-d array First key is the namespace, second is the DB key,...
Definition: LinkBatch.php:191
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.
Definition: LinkBatch.php:244
doQuery()
Perform the existence test query, return a result wrapper with page_id fields.
Definition: LinkBatch.php:329
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition: LinkCache.php:42
static getSelectFields()
Fields that LinkCache needs to select.
Definition: LinkCache.php:357
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.
Definition: TitleValue.php:40
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:40
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