MediaWiki  master
ClassicInterwikiLookup.php
Go to the documentation of this file.
1 <?php
24 
26 use Cdb\Reader as CdbReader;
27 use Hooks;
28 use Interwiki;
29 use Language;
30 use WikiMap;
31 use MapCacheLRU;
32 use WANObjectCache;
34 
48 
52  private $localCache;
53 
57  private $contLang;
58 
62  private $objectCache;
63 
68 
72  private $cdbData;
73 
78 
82  private $fallbackSite;
83 
87  private $cdbReader = null;
88 
92  private $thisSite = null;
93 
107  function __construct(
111  $cdbData,
114  ) {
115  $this->localCache = new MapCacheLRU( 100 );
116 
117  $this->contLang = $contLang;
118  $this->objectCache = $objectCache;
119  $this->objectCacheExpiry = $objectCacheExpiry;
120  $this->cdbData = $cdbData;
121  $this->interwikiScopes = $interwikiScopes;
122  $this->fallbackSite = $fallbackSite;
123  }
124 
131  public function isValidInterwiki( $prefix ) {
132  $result = $this->fetch( $prefix );
133 
134  return (bool)$result;
135  }
136 
143  public function fetch( $prefix ) {
144  if ( $prefix == '' ) {
145  return null;
146  }
147 
148  $prefix = $this->contLang->lc( $prefix );
149  if ( $this->localCache->has( $prefix ) ) {
150  return $this->localCache->get( $prefix );
151  }
152 
153  if ( $this->cdbData ) {
154  $iw = $this->getInterwikiCached( $prefix );
155  } else {
156  $iw = $this->load( $prefix );
157  if ( !$iw ) {
158  $iw = false;
159  }
160  }
161  $this->localCache->set( $prefix, $iw );
162 
163  return $iw;
164  }
165 
171  public function resetLocalCache() {
172  $this->localCache->clear();
173  }
174 
179  public function invalidateCache( $prefix ) {
180  $this->localCache->clear( $prefix );
181 
182  $key = $this->objectCache->makeKey( 'interwiki', $prefix );
183  $this->objectCache->delete( $key );
184  }
185 
194  private function getInterwikiCached( $prefix ) {
195  $value = $this->getInterwikiCacheEntry( $prefix );
196 
197  if ( $value ) {
198  // Split values
199  list( $local, $url ) = explode( ' ', $value, 2 );
200  return new Interwiki( $prefix, $url, '', '', (int)$local );
201  } else {
202  return false;
203  }
204  }
205 
214  private function getInterwikiCacheEntry( $prefix ) {
215  wfDebug( __METHOD__ . "( $prefix )\n" );
216 
218 
219  $value = false;
220  try {
221  // Resolve site name
222  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
223  $this->thisSite = $this->getCacheValue( '__sites:' . $wikiId );
224  if ( $this->thisSite == '' ) {
225  $this->thisSite = $this->fallbackSite;
226  }
227  }
228 
229  $value = $this->getCacheValue( $wikiId . ':' . $prefix );
230  // Site level
231  if ( $value == '' && $this->interwikiScopes >= 3 ) {
232  $value = $this->getCacheValue( "_{$this->thisSite}:{$prefix}" );
233  }
234  // Global Level
235  if ( $value == '' && $this->interwikiScopes >= 2 ) {
236  $value = $this->getCacheValue( "__global:{$prefix}" );
237  }
238  if ( $value == 'undef' ) {
239  $value = '';
240  }
241  } catch ( CdbException $e ) {
242  wfDebug( __METHOD__ . ": CdbException caught, error message was "
243  . $e->getMessage() );
244  }
245 
246  return $value;
247  }
248 
249  private function getCacheValue( $key ) {
250  if ( $this->cdbReader === null ) {
251  if ( is_string( $this->cdbData ) ) {
252  $this->cdbReader = \Cdb\Reader::open( $this->cdbData );
253  } elseif ( is_array( $this->cdbData ) ) {
254  $this->cdbReader = new \Cdb\Reader\Hash( $this->cdbData );
255  } else {
256  $this->cdbReader = false;
257  }
258  }
259 
260  if ( $this->cdbReader ) {
261  return $this->cdbReader->get( $key );
262  } else {
263  return false;
264  }
265  }
266 
273  private function load( $prefix ) {
274  $iwData = [];
275  if ( !Hooks::run( 'InterwikiLoadPrefix', [ $prefix, &$iwData ] ) ) {
276  return $this->loadFromArray( $iwData );
277  }
278 
279  if ( is_array( $iwData ) ) {
280  $iw = $this->loadFromArray( $iwData );
281  if ( $iw ) {
282  return $iw; // handled by hook
283  }
284  }
285 
286  $fname = __METHOD__;
287  $iwData = $this->objectCache->getWithSetCallback(
288  $this->objectCache->makeKey( 'interwiki', $prefix ),
290  function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
291  $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
292 
293  $setOpts += Database::getCacheSetOptions( $dbr );
294 
295  $row = $dbr->selectRow(
296  'interwiki',
297  self::selectFields(),
298  [ 'iw_prefix' => $prefix ],
299  $fname
300  );
301 
302  return $row ? (array)$row : '!NONEXISTENT';
303  }
304  );
305 
306  if ( is_array( $iwData ) ) {
307  return $this->loadFromArray( $iwData ) ?: false;
308  }
309 
310  return false;
311  }
312 
319  private function loadFromArray( $mc ) {
320  if ( isset( $mc['iw_url'] ) ) {
321  $url = $mc['iw_url'];
322  $local = $mc['iw_local'] ?? 0;
323  $trans = $mc['iw_trans'] ?? 0;
324  $api = $mc['iw_api'] ?? '';
325  $wikiId = $mc['iw_wikiid'] ?? '';
326 
327  return new Interwiki( null, $url, $api, $wikiId, $local, $trans );
328  }
329 
330  return false;
331  }
332 
339  private function getAllPrefixesCached( $local ) {
340  wfDebug( __METHOD__ . "()\n" );
341 
343 
344  $data = [];
345  try {
346  /* Resolve site name */
347  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
348  $site = $this->getCacheValue( '__sites:' . $wikiId );
349 
350  if ( $site == '' ) {
351  $this->thisSite = $this->fallbackSite;
352  } else {
353  $this->thisSite = $site;
354  }
355  }
356 
357  // List of interwiki sources
358  $sources = [];
359  // Global Level
360  if ( $this->interwikiScopes >= 2 ) {
361  $sources[] = '__global';
362  }
363  // Site level
364  if ( $this->interwikiScopes >= 3 ) {
365  $sources[] = '_' . $this->thisSite;
366  }
367  $sources[] = $wikiId;
368 
369  foreach ( $sources as $source ) {
370  $list = $this->getCacheValue( '__list:' . $source );
371  foreach ( explode( ' ', $list ) as $iw_prefix ) {
372  $row = $this->getCacheValue( "{$source}:{$iw_prefix}" );
373  if ( !$row ) {
374  continue;
375  }
376 
377  list( $iw_local, $iw_url ) = explode( ' ', $row );
378 
379  if ( $local !== null && $local != $iw_local ) {
380  continue;
381  }
382 
383  $data[$iw_prefix] = [
384  'iw_prefix' => $iw_prefix,
385  'iw_url' => $iw_url,
386  'iw_local' => $iw_local,
387  ];
388  }
389  }
390  } catch ( CdbException $e ) {
391  wfDebug( __METHOD__ . ": CdbException caught, error message was "
392  . $e->getMessage() );
393  }
394 
395  return array_values( $data );
396  }
397 
404  private function getAllPrefixesDB( $local ) {
405  $db = wfGetDB( DB_REPLICA ); // TODO: inject DB LoadBalancer
406 
407  $where = [];
408 
409  if ( $local !== null ) {
410  if ( $local == 1 ) {
411  $where['iw_local'] = 1;
412  } elseif ( $local == 0 ) {
413  $where['iw_local'] = 0;
414  }
415  }
416 
417  $res = $db->select( 'interwiki',
418  self::selectFields(),
419  $where, __METHOD__, [ 'ORDER BY' => 'iw_prefix' ]
420  );
421 
422  $retval = [];
423  foreach ( $res as $row ) {
424  $retval[] = (array)$row;
425  }
426 
427  return $retval;
428  }
429 
436  public function getAllPrefixes( $local = null ) {
437  if ( $this->cdbData ) {
438  return $this->getAllPrefixesCached( $local );
439  }
440 
441  return $this->getAllPrefixesDB( $local );
442  }
443 
449  private static function selectFields() {
450  return [
451  'iw_prefix',
452  'iw_url',
453  'iw_api',
454  'iw_wikiid',
455  'iw_local',
456  'iw_trans'
457  ];
458  }
459 
460 }
getAllPrefixesDB( $local)
Fetch all interwiki prefixes from DB.
static selectFields()
Return the list of interwiki fields that should be selected to create a new Interwiki object...
getInterwikiCacheEntry( $prefix)
Get entry from interwiki cache.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
static getWikiIdFromDbDomain( $domain)
Get the wiki ID of a database domain.
Definition: WikiMap.php:269
$source
InterwikiLookup implementing the "classic" interwiki storage (hardcoded up to MW 1.26).
getAllPrefixes( $local=null)
Returns all interwiki prefixes.
isValidInterwiki( $prefix)
Check whether an interwiki prefix exists.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
invalidateCache( $prefix)
Purge the in-process and object cache for an interwiki prefix.
fetch( $prefix)
Fetch an Interwiki object.
loadFromArray( $mc)
Fill in member variables from an array (e.g.
Service interface for looking up Interwiki records.
Value object for representing interwiki records.
Definition: Interwiki.php:27
static getCurrentWikiDbDomain()
Definition: WikiMap.php:293
__construct(Language $contLang, WANObjectCache $objectCache, $objectCacheExpiry, $cdbData, $interwikiScopes, $fallbackSite)
Copyright (C) 2018 Kunal Mehta legoktm@member.fsf.org
load( $prefix)
Load the interwiki, trying first memcached then the DB.
getAllPrefixesCached( $local)
Fetch all interwiki prefixes from interwiki cache.
static getCacheSetOptions(IDatabase $db1, IDatabase $db2=null)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Definition: Database.php:4432
getInterwikiCached( $prefix)
Fetch interwiki prefix data from local cache in constant database.
resetLocalCache()
Resets locally cached Interwiki objects.
const DB_REPLICA
Definition: defines.php:25
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200