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 $dbProvider;
84 
86  private $linksMigration;
87 
89  private $logger;
90 
104  public function __construct(
105  iterable $arr,
106  LinkCache $linkCache,
107  TitleFormatter $titleFormatter,
108  Language $contentLanguage,
109  GenderCache $genderCache,
110  IConnectionProvider $dbProvider,
111  LinksMigration $linksMigration,
112  LoggerInterface $logger
113  ) {
114  $this->linkCache = $linkCache;
115  $this->titleFormatter = $titleFormatter;
116  $this->contentLanguage = $contentLanguage;
117  $this->genderCache = $genderCache;
118  $this->dbProvider = $dbProvider;
119  $this->linksMigration = $linksMigration;
120  $this->logger = $logger;
121 
122  foreach ( $arr as $item ) {
123  $this->addObj( $item );
124  }
125  }
126 
135  public function setCaller( $caller ) {
136  $this->caller = $caller;
137 
138  return $this;
139  }
140 
144  public function addObj( $link ) {
145  if ( !$link ) {
146  // Don't die if we got null, just skip. There is nothing to do anyway.
147  // For now, let's avoid things like T282180. We should be more strict in the future.
148  $this->logger->warning(
149  'Skipping null link, probably due to a bad title.',
150  [ 'exception' => new RuntimeException() ]
151  );
152  return;
153  }
154  if ( $link instanceof LinkTarget && $link->isExternal() ) {
155  $this->logger->warning(
156  'Skipping interwiki link',
157  [ 'exception' => new RuntimeException() ]
158  );
159  return;
160  }
161 
162  Assert::parameterType( [ LinkTarget::class, PageReference::class ], $link, '$link' );
163  $this->add( $link->getNamespace(), $link->getDBkey() );
164  }
165 
170  public function add( $ns, $dbkey ) {
171  if ( $ns < 0 || $dbkey === '' ) {
172  // T137083
173  return;
174  }
175  $this->data[$ns][strtr( $dbkey, ' ', '_' )] = 1;
176  }
177 
184  public function setArray( $array ) {
185  $this->data = $array;
186  }
187 
193  public function isEmpty() {
194  return $this->getSize() == 0;
195  }
196 
202  public function getSize() {
203  return count( $this->data );
204  }
205 
211  public function execute() {
212  return $this->executeInto( $this->linkCache );
213  }
214 
222  public function getPageIdentities(): array {
223  if ( $this->pageIdentities === null ) {
224  $this->execute();
225  }
226 
227  return $this->pageIdentities;
228  }
229 
237  protected function executeInto( $cache ) {
238  $res = $this->doQuery();
239  $this->doGenderQuery();
240  return $this->addResultToCache( $cache, $res );
241  }
242 
253  public function addResultToCache( $cache, $res ) {
254  if ( !$res ) {
255  return [];
256  }
257 
258  // For each returned entry, add it to the list of good links, and remove it from $remaining
259 
260  $this->pageIdentities ??= [];
261 
262  $ids = [];
263  $remaining = $this->data;
264  foreach ( $res as $row ) {
265  try {
266  $title = new TitleValue( (int)$row->page_namespace, $row->page_title );
267 
268  $cache->addGoodLinkObjFromRow( $title, $row );
269  $pdbk = $this->titleFormatter->getPrefixedDBkey( $title );
270  $ids[$pdbk] = $row->page_id;
271 
272  $pageIdentity = new PageIdentityValue(
273  (int)$row->page_id,
274  (int)$row->page_namespace,
275  $row->page_title,
276  ProperPageIdentity::LOCAL
277  );
278 
279  $key = CacheKeyHelper::getKeyForPage( $pageIdentity );
280  $this->pageIdentities[$key] = $pageIdentity;
281  } catch ( InvalidArgumentException $ex ) {
282  $this->logger->warning(
283  'Encountered invalid title',
284  [ 'title_namespace' => $row->page_namespace, 'title_dbkey' => $row->page_title ]
285  );
286  }
287 
288  unset( $remaining[$row->page_namespace][$row->page_title] );
289  }
290 
291  // The remaining links in $data are bad links, register them as such
292  foreach ( $remaining as $ns => $dbkeys ) {
293  foreach ( $dbkeys as $dbkey => $unused ) {
294  try {
295  $title = new TitleValue( (int)$ns, (string)$dbkey );
296 
297  $cache->addBadLinkObj( $title );
298  $pdbk = $this->titleFormatter->getPrefixedDBkey( $title );
299  $ids[$pdbk] = 0;
300 
301  $pageIdentity = new PageIdentityValue( 0, (int)$ns, $dbkey, ProperPageIdentity::LOCAL );
302  $key = CacheKeyHelper::getKeyForPage( $pageIdentity );
303  $this->pageIdentities[$key] = $pageIdentity;
304  } catch ( InvalidArgumentException $ex ) {
305  $this->logger->warning(
306  'Encountered invalid title',
307  [ 'title_namespace' => $ns, 'title_dbkey' => $dbkey ]
308  );
309  }
310  }
311  }
312 
313  return $ids;
314  }
315 
320  public function doQuery() {
321  if ( $this->isEmpty() ) {
322  return false;
323  }
324 
325  // This is similar to LinkHolderArray::replaceInternal
326  $dbr = $this->dbProvider->getReplicaDatabase();
327  $queryBuilder = $dbr->newSelectQueryBuilder()
328  ->select( LinkCache::getSelectFields() )
329  ->from( 'page' )
330  ->where( $this->constructSet( 'page', $dbr ) );
331 
332  $caller = __METHOD__;
333  if ( strval( $this->caller ) !== '' ) {
334  $caller .= " (for {$this->caller})";
335  }
336 
337  return $queryBuilder->caller( $caller )->fetchResultSet();
338  }
339 
345  public function doGenderQuery() {
346  if ( $this->isEmpty() || !$this->contentLanguage->needsGenderDistinction() ) {
347  return false;
348  }
349 
350  $this->genderCache->doLinkBatch( $this->data, $this->caller );
351 
352  return true;
353  }
354 
366  public function constructSet( $prefix, $db ) {
367  if ( isset( $this->linksMigration::$prefixToTableMapping[$prefix] ) ) {
368  [ $blNamespace, $blTitle ] = $this->linksMigration->getTitleFields(
369  $this->linksMigration::$prefixToTableMapping[$prefix]
370  );
371  } else {
372  $blNamespace = "{$prefix}_namespace";
373  $blTitle = "{$prefix}_title";
374  }
375  return $db->makeWhereFrom2d( $this->data, $blNamespace, $blTitle );
376  }
377 }
if(!defined('MW_SETUP_CALLBACK'))
Definition: WebStart.php:88
Caches user genders when needed to use correct namespace aliases.
Definition: GenderCache.php:35
Base class for language-specific code.
Definition: Language.php:61
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:345
__construct(iterable $arr, LinkCache $linkCache, TitleFormatter $titleFormatter, Language $contentLanguage, GenderCache $genderCache, IConnectionProvider $dbProvider, LinksMigration $linksMigration, LoggerInterface $logger)
Definition: LinkBatch.php:104
add( $ns, $dbkey)
Definition: LinkBatch.php:170
getSize()
Returns the size of the batch.
Definition: LinkBatch.php:202
addObj( $link)
Definition: LinkBatch.php:144
addResultToCache( $cache, $res)
Add a result wrapper containing IDs and titles to a LinkCache object.
Definition: LinkBatch.php:253
isEmpty()
Returns true if no pages have been added, false otherwise.
Definition: LinkBatch.php:193
setCaller( $caller)
Use ->setCaller( METHOD ) to indicate which code is using this class.
Definition: LinkBatch.php:135
constructSet( $prefix, $db)
Construct a WHERE clause which will match all the given titles.
Definition: LinkBatch.php:366
getPageIdentities()
Do the query, add the results to the LinkCache object, and return ProperPageIdentity instances corres...
Definition: LinkBatch.php:222
execute()
Do the query and add the results to the LinkCache object.
Definition: LinkBatch.php:211
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:184
string null $caller
For debugging which method is using this class.
Definition: LinkBatch.php:58
array< int, array< string, mixed > > $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:237
doQuery()
Perform the existence test query, return a result wrapper with page_id fields.
Definition: LinkBatch.php:320
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition: LinkCache.php:45
static getSelectFields()
Fields that LinkCache needs to select.
Definition: LinkCache.php:344
Helper class for mapping value objects representing basic entities to cache keys.
Service for compat reading of links tables.
Immutable value object representing a page identity.
Represents the target of a wiki link.
Definition: TitleValue.php:44
Represents the target of a wiki link.
Definition: LinkTarget.php:30
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.
Provide primary and replica IDatabase connections.
Result wrapper for grabbing data queried from an IDatabase object.
Interface for query language.