MediaWiki  master
ClassicInterwikiLookup.php
Go to the documentation of this file.
1 <?php
24 
25 use Cdb\Exception as CdbException;
26 use Cdb\Reader as CdbReader;
27 use Interwiki;
28 use Language;
29 use MapCacheLRU;
32 use WANObjectCache;
33 use WikiMap;
36 
50 
54  private $localCache;
55 
59  private $contLang;
60 
64  private $objectCache;
65 
70 
74  private $cdbData;
75 
80 
84  private $fallbackSite;
85 
89  private $cdbReader = null;
90 
94  private $thisSite = null;
95 
97  private $hookRunner;
98 
100  private $loadBalancer;
101 
117  public function __construct(
120  HookContainer $hookContainer,
123  $cdbData,
126  ) {
127  $this->localCache = new MapCacheLRU( 100 );
128 
129  $this->contLang = $contLang;
130  $this->objectCache = $objectCache;
131  $this->hookRunner = new HookRunner( $hookContainer );
132  $this->loadBalancer = $loadBalancer;
133  $this->objectCacheExpiry = $objectCacheExpiry;
134  $this->cdbData = $cdbData;
135  $this->interwikiScopes = $interwikiScopes;
136  $this->fallbackSite = $fallbackSite;
137  }
138 
145  public function isValidInterwiki( $prefix ) {
146  $result = $this->fetch( $prefix );
147 
148  return (bool)$result;
149  }
150 
157  public function fetch( $prefix ) {
158  if ( $prefix == '' ) {
159  return null;
160  }
161 
162  $prefix = $this->contLang->lc( $prefix );
163  return $this->localCache->getWithSetCallback(
164  $prefix,
165  function () use ( $prefix ) {
166  if ( $this->cdbData ) {
167  $iw = $this->getInterwikiCached( $prefix );
168  } else {
169  $iw = $this->load( $prefix );
170  if ( !$iw ) {
171  $iw = false;
172  }
173  }
174  return $iw;
175  }
176  );
177  }
178 
184  public function resetLocalCache() {
185  $this->localCache->clear();
186  }
187 
192  public function invalidateCache( $prefix ) {
193  $this->localCache->clear( $prefix );
194 
195  $key = $this->objectCache->makeKey( 'interwiki', $prefix );
196  $this->objectCache->delete( $key );
197  }
198 
207  private function getInterwikiCached( $prefix ) {
208  $value = $this->getInterwikiCacheEntry( $prefix );
209 
210  if ( $value ) {
211  // Split values
212  list( $local, $url ) = explode( ' ', $value, 2 );
213  return new Interwiki( $prefix, $url, '', '', (int)$local );
214  } else {
215  return false;
216  }
217  }
218 
227  private function getInterwikiCacheEntry( $prefix ) {
228  wfDebug( __METHOD__ . "( $prefix )" );
229 
230  $wikiId = WikiMap::getCurrentWikiId();
231 
232  $value = false;
233  try {
234  // Resolve site name
235  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
236  $this->thisSite = $this->getCacheValue( '__sites:' . $wikiId );
237  if ( $this->thisSite == '' ) {
238  $this->thisSite = $this->fallbackSite;
239  }
240  }
241 
242  $value = $this->getCacheValue( $wikiId . ':' . $prefix );
243  // Site level
244  if ( $value == '' && $this->interwikiScopes >= 3 ) {
245  $value = $this->getCacheValue( "_{$this->thisSite}:{$prefix}" );
246  }
247  // Global Level
248  if ( $value == '' && $this->interwikiScopes >= 2 ) {
249  $value = $this->getCacheValue( "__global:{$prefix}" );
250  }
251  if ( $value == 'undef' ) {
252  $value = '';
253  }
254  } catch ( CdbException $e ) {
255  wfDebug( __METHOD__ . ": CdbException caught, error message was "
256  . $e->getMessage() );
257  }
258 
259  return $value;
260  }
261 
262  private function getCacheValue( $key ) {
263  if ( $this->cdbReader === null ) {
264  if ( is_string( $this->cdbData ) ) {
265  $this->cdbReader = \Cdb\Reader::open( $this->cdbData );
266  } elseif ( is_array( $this->cdbData ) ) {
267  $this->cdbReader = new \Cdb\Reader\Hash( $this->cdbData );
268  } else {
269  $this->cdbReader = false;
270  }
271  }
272 
273  if ( $this->cdbReader ) {
274  return $this->cdbReader->get( $key );
275  } else {
276  return false;
277  }
278  }
279 
286  private function load( $prefix ) {
287  $iwData = [];
288  if ( !$this->hookRunner->onInterwikiLoadPrefix( $prefix, $iwData ) ) {
289  return $this->loadFromArray( $iwData );
290  }
291 
292  if ( is_array( $iwData ) ) {
293  $iw = $this->loadFromArray( $iwData );
294  if ( $iw ) {
295  return $iw; // handled by hook
296  }
297  }
298 
299  $fname = __METHOD__;
300  $iwData = $this->objectCache->getWithSetCallback(
301  $this->objectCache->makeKey( 'interwiki', $prefix ),
302  $this->objectCacheExpiry,
303  function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
304  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
305 
306  $setOpts += Database::getCacheSetOptions( $dbr );
307 
308  $row = $dbr->selectRow(
309  'interwiki',
310  self::selectFields(),
311  [ 'iw_prefix' => $prefix ],
312  $fname
313  );
314 
315  return $row ? (array)$row : '!NONEXISTENT';
316  }
317  );
318 
319  if ( is_array( $iwData ) ) {
320  return $this->loadFromArray( $iwData ) ?: false;
321  }
322 
323  return false;
324  }
325 
332  private function loadFromArray( $mc ) {
333  if ( isset( $mc['iw_url'] ) ) {
334  $url = $mc['iw_url'];
335  $local = $mc['iw_local'] ?? 0;
336  $trans = $mc['iw_trans'] ?? 0;
337  $api = $mc['iw_api'] ?? '';
338  $wikiId = $mc['iw_wikiid'] ?? '';
339 
340  return new Interwiki( null, $url, $api, $wikiId, $local, $trans );
341  }
342 
343  return false;
344  }
345 
352  private function getAllPrefixesCached( $local ) {
353  wfDebug( __METHOD__ . "()" );
354 
355  $wikiId = WikiMap::getCurrentWikiId();
356 
357  $data = [];
358  try {
359  /* Resolve site name */
360  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
361  $site = $this->getCacheValue( '__sites:' . $wikiId );
362 
363  if ( $site == '' ) {
364  $this->thisSite = $this->fallbackSite;
365  } else {
366  $this->thisSite = $site;
367  }
368  }
369 
370  // List of interwiki sources
371  $sources = [];
372  // Global Level
373  if ( $this->interwikiScopes >= 2 ) {
374  $sources[] = '__global';
375  }
376  // Site level
377  if ( $this->interwikiScopes >= 3 ) {
378  $sources[] = '_' . $this->thisSite;
379  }
380  $sources[] = $wikiId;
381 
382  foreach ( $sources as $source ) {
383  $list = $this->getCacheValue( '__list:' . $source );
384  foreach ( explode( ' ', $list ) as $iw_prefix ) {
385  $row = $this->getCacheValue( "{$source}:{$iw_prefix}" );
386  if ( !$row ) {
387  continue;
388  }
389 
390  list( $iw_local, $iw_url ) = explode( ' ', $row );
391 
392  if ( $local !== null && $local != $iw_local ) {
393  continue;
394  }
395 
396  $data[$iw_prefix] = [
397  'iw_prefix' => $iw_prefix,
398  'iw_url' => $iw_url,
399  'iw_local' => $iw_local,
400  ];
401  }
402  }
403  } catch ( CdbException $e ) {
404  wfDebug( __METHOD__ . ": CdbException caught, error message was "
405  . $e->getMessage() );
406  }
407 
408  return array_values( $data );
409  }
410 
424  public static function buildCdbHash(
425  array $allPrefixes, int $scope = 1, ?string $thisSite = null
426  ): array {
427  $result = [];
428  $wikiId = WikiMap::getCurrentWikiId();
429  $keyPrefix = ( $scope >= 2 ) ? '__global' : $wikiId;
430  if ( $scope >= 3 && $thisSite ) {
431  $result[ "__sites:$wikiId" ] = $thisSite;
432  $keyPrefix = "_$thisSite";
433  }
434  $list = [];
435  foreach ( $allPrefixes as $iwInfo ) {
436  $prefix = $iwInfo['iw_prefix'];
437  $result["$keyPrefix:$prefix"] = implode( ' ', [
438  $iwInfo['iw_local'] ?? 0, $iwInfo['iw_url']
439  ] );
440  $list[] = $prefix;
441  }
442  $result["__list:$keyPrefix"] = implode( ' ', $list );
443  $result["__list:__sites"] = $wikiId;
444  return $result;
445  }
446 
453  private function getAllPrefixesDB( $local ) {
454  $db = $this->loadBalancer->getConnectionRef( DB_REPLICA );
455 
456  $where = [];
457 
458  if ( $local !== null ) {
459  if ( $local == 1 ) {
460  $where['iw_local'] = 1;
461  } elseif ( $local == 0 ) {
462  $where['iw_local'] = 0;
463  }
464  }
465 
466  $res = $db->select( 'interwiki',
467  self::selectFields(),
468  $where, __METHOD__, [ 'ORDER BY' => 'iw_prefix' ]
469  );
470 
471  $retval = [];
472  foreach ( $res as $row ) {
473  $retval[] = (array)$row;
474  }
475 
476  return $retval;
477  }
478 
485  public function getAllPrefixes( $local = null ) {
486  if ( $this->cdbData ) {
487  return $this->getAllPrefixesCached( $local );
488  }
489 
490  return $this->getAllPrefixesDB( $local );
491  }
492 
498  private static function selectFields() {
499  return [
500  'iw_prefix',
501  'iw_url',
502  'iw_api',
503  'iw_wikiid',
504  'iw_local',
505  'iw_trans'
506  ];
507  }
508 
509 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:52
MediaWiki\Interwiki\ClassicInterwikiLookup\$localCache
MapCacheLRU $localCache
Definition: ClassicInterwikiLookup.php:54
MediaWiki\Interwiki\ClassicInterwikiLookup\isValidInterwiki
isValidInterwiki( $prefix)
Check whether an interwiki prefix exists.
Definition: ClassicInterwikiLookup.php:145
MediaWiki\Interwiki\ClassicInterwikiLookup
InterwikiLookup implementing the "classic" interwiki storage (hardcoded up to MW 1....
Definition: ClassicInterwikiLookup.php:49
MediaWiki\Interwiki\ClassicInterwikiLookup\getAllPrefixesCached
getAllPrefixesCached( $local)
Fetch all interwiki prefixes from interwiki cache.
Definition: ClassicInterwikiLookup.php:352
MediaWiki\Interwiki\ClassicInterwikiLookup\resetLocalCache
resetLocalCache()
Resets locally cached Interwiki objects.
Definition: ClassicInterwikiLookup.php:184
MediaWiki\Interwiki\ClassicInterwikiLookup\__construct
__construct(Language $contLang, WANObjectCache $objectCache, HookContainer $hookContainer, ILoadBalancer $loadBalancer, $objectCacheExpiry, $cdbData, $interwikiScopes, $fallbackSite)
Definition: ClassicInterwikiLookup.php:117
MediaWiki\Interwiki\ClassicInterwikiLookup\$thisSite
string null $thisSite
Definition: ClassicInterwikiLookup.php:94
$res
$res
Definition: testCompression.php:57
WikiMap\getCurrentWikiId
static getCurrentWikiId()
Definition: WikiMap.php:303
MediaWiki\Interwiki\ClassicInterwikiLookup\$interwikiScopes
int $interwikiScopes
Definition: ClassicInterwikiLookup.php:79
MediaWiki\Interwiki\ClassicInterwikiLookup\buildCdbHash
static buildCdbHash(array $allPrefixes, int $scope=1, ?string $thisSite=null)
Given the array returned by getAllPrefixes(), build a PHP hash which can be given to \Cdb\Reader\Hash...
Definition: ClassicInterwikiLookup.php:424
MediaWiki\Interwiki\ClassicInterwikiLookup\$contLang
Language $contLang
Definition: ClassicInterwikiLookup.php:59
MediaWiki\Interwiki\ClassicInterwikiLookup\$loadBalancer
ILoadBalancer $loadBalancer
Definition: ClassicInterwikiLookup.php:100
$dbr
$dbr
Definition: testCompression.php:54
MediaWiki\Interwiki\ClassicInterwikiLookup\getAllPrefixesDB
getAllPrefixesDB( $local)
Fetch all interwiki prefixes from DB.
Definition: ClassicInterwikiLookup.php:453
Wikimedia\Rdbms\Database\getCacheSetOptions
static getCacheSetOptions(?IDatabase ... $dbs)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Definition: Database.php:5354
MediaWiki\Interwiki\ClassicInterwikiLookup\getInterwikiCacheEntry
getInterwikiCacheEntry( $prefix)
Get entry from interwiki cache.
Definition: ClassicInterwikiLookup.php:227
MediaWiki\Interwiki\ClassicInterwikiLookup\load
load( $prefix)
Load the interwiki, trying first memcached then the DB.
Definition: ClassicInterwikiLookup.php:286
MediaWiki\Interwiki\InterwikiLookup
Service interface for looking up Interwiki records.
Definition: InterwikiLookup.php:32
MediaWiki\Interwiki\ClassicInterwikiLookup\$objectCache
WANObjectCache $objectCache
Definition: ClassicInterwikiLookup.php:64
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:36
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MediaWiki\Interwiki\ClassicInterwikiLookup\$hookRunner
HookRunner $hookRunner
Definition: ClassicInterwikiLookup.php:97
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:894
MediaWiki\Interwiki\ClassicInterwikiLookup\$cdbData
bool array string $cdbData
Definition: ClassicInterwikiLookup.php:74
MediaWiki\Interwiki\ClassicInterwikiLookup\fetch
fetch( $prefix)
Fetch an Interwiki object.
Definition: ClassicInterwikiLookup.php:157
MediaWiki\Interwiki\ClassicInterwikiLookup\getAllPrefixes
getAllPrefixes( $local=null)
Returns all interwiki prefixes.
Definition: ClassicInterwikiLookup.php:485
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:131
MediaWiki\Interwiki\ClassicInterwikiLookup\$objectCacheExpiry
int $objectCacheExpiry
Definition: ClassicInterwikiLookup.php:69
MediaWiki\Interwiki\ClassicInterwikiLookup\$fallbackSite
string $fallbackSite
Definition: ClassicInterwikiLookup.php:84
MediaWiki\Interwiki\ClassicInterwikiLookup\invalidateCache
invalidateCache( $prefix)
Purge the in-process and object cache for an interwiki prefix.
Definition: ClassicInterwikiLookup.php:192
WikiMap
Helper tools for dealing with other locally-hosted wikis.
Definition: WikiMap.php:29
MediaWiki\Interwiki\ClassicInterwikiLookup\selectFields
static selectFields()
Return the list of interwiki fields that should be selected to create a new Interwiki object.
Definition: ClassicInterwikiLookup.php:498
Interwiki
Value object for representing interwiki records.
Definition: Interwiki.php:26
MediaWiki\Interwiki
Copyright (C) 2018 Kunal Mehta legoktm@debian.org
Definition: ClassicInterwikiLookup.php:23
$source
$source
Definition: mwdoc-filter.php:34
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\Interwiki\ClassicInterwikiLookup\$cdbReader
CdbReader null $cdbReader
Definition: ClassicInterwikiLookup.php:89
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:557
MediaWiki\Interwiki\ClassicInterwikiLookup\getInterwikiCached
getInterwikiCached( $prefix)
Fetch interwiki prefix data from local cache in constant database.
Definition: ClassicInterwikiLookup.php:207
MediaWiki\Interwiki\ClassicInterwikiLookup\getCacheValue
getCacheValue( $key)
Definition: ClassicInterwikiLookup.php:262
MediaWiki\Interwiki\ClassicInterwikiLookup\loadFromArray
loadFromArray( $mc)
Fill in member variables from an array (e.g.
Definition: ClassicInterwikiLookup.php:332
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81