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  if ( $this->localCache->has( $prefix ) ) {
164  return $this->localCache->get( $prefix );
165  }
166 
167  if ( $this->cdbData ) {
168  $iw = $this->getInterwikiCached( $prefix );
169  } else {
170  $iw = $this->load( $prefix );
171  if ( !$iw ) {
172  $iw = false;
173  }
174  }
175  $this->localCache->set( $prefix, $iw );
176 
177  return $iw;
178  }
179 
185  public function resetLocalCache() {
186  $this->localCache->clear();
187  }
188 
193  public function invalidateCache( $prefix ) {
194  $this->localCache->clear( $prefix );
195 
196  $key = $this->objectCache->makeKey( 'interwiki', $prefix );
197  $this->objectCache->delete( $key );
198  }
199 
208  private function getInterwikiCached( $prefix ) {
209  $value = $this->getInterwikiCacheEntry( $prefix );
210 
211  if ( $value ) {
212  // Split values
213  list( $local, $url ) = explode( ' ', $value, 2 );
214  return new Interwiki( $prefix, $url, '', '', (int)$local );
215  } else {
216  return false;
217  }
218  }
219 
228  private function getInterwikiCacheEntry( $prefix ) {
229  wfDebug( __METHOD__ . "( $prefix )" );
230 
231  $wikiId = WikiMap::getCurrentWikiId();
232 
233  $value = false;
234  try {
235  // Resolve site name
236  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
237  $this->thisSite = $this->getCacheValue( '__sites:' . $wikiId );
238  if ( $this->thisSite == '' ) {
239  $this->thisSite = $this->fallbackSite;
240  }
241  }
242 
243  $value = $this->getCacheValue( $wikiId . ':' . $prefix );
244  // Site level
245  if ( $value == '' && $this->interwikiScopes >= 3 ) {
246  $value = $this->getCacheValue( "_{$this->thisSite}:{$prefix}" );
247  }
248  // Global Level
249  if ( $value == '' && $this->interwikiScopes >= 2 ) {
250  $value = $this->getCacheValue( "__global:{$prefix}" );
251  }
252  if ( $value == 'undef' ) {
253  $value = '';
254  }
255  } catch ( CdbException $e ) {
256  wfDebug( __METHOD__ . ": CdbException caught, error message was "
257  . $e->getMessage() );
258  }
259 
260  return $value;
261  }
262 
263  private function getCacheValue( $key ) {
264  if ( $this->cdbReader === null ) {
265  if ( is_string( $this->cdbData ) ) {
266  $this->cdbReader = \Cdb\Reader::open( $this->cdbData );
267  } elseif ( is_array( $this->cdbData ) ) {
268  $this->cdbReader = new \Cdb\Reader\Hash( $this->cdbData );
269  } else {
270  $this->cdbReader = false;
271  }
272  }
273 
274  if ( $this->cdbReader ) {
275  return $this->cdbReader->get( $key );
276  } else {
277  return false;
278  }
279  }
280 
287  private function load( $prefix ) {
288  $iwData = [];
289  if ( !$this->hookRunner->onInterwikiLoadPrefix( $prefix, $iwData ) ) {
290  return $this->loadFromArray( $iwData );
291  }
292 
293  if ( is_array( $iwData ) ) {
294  $iw = $this->loadFromArray( $iwData );
295  if ( $iw ) {
296  return $iw; // handled by hook
297  }
298  }
299 
300  $fname = __METHOD__;
301  $iwData = $this->objectCache->getWithSetCallback(
302  $this->objectCache->makeKey( 'interwiki', $prefix ),
303  $this->objectCacheExpiry,
304  function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
305  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
306 
307  $setOpts += Database::getCacheSetOptions( $dbr );
308 
309  $row = $dbr->selectRow(
310  'interwiki',
311  self::selectFields(),
312  [ 'iw_prefix' => $prefix ],
313  $fname
314  );
315 
316  return $row ? (array)$row : '!NONEXISTENT';
317  }
318  );
319 
320  if ( is_array( $iwData ) ) {
321  return $this->loadFromArray( $iwData ) ?: false;
322  }
323 
324  return false;
325  }
326 
333  private function loadFromArray( $mc ) {
334  if ( isset( $mc['iw_url'] ) ) {
335  $url = $mc['iw_url'];
336  $local = $mc['iw_local'] ?? 0;
337  $trans = $mc['iw_trans'] ?? 0;
338  $api = $mc['iw_api'] ?? '';
339  $wikiId = $mc['iw_wikiid'] ?? '';
340 
341  return new Interwiki( null, $url, $api, $wikiId, $local, $trans );
342  }
343 
344  return false;
345  }
346 
353  private function getAllPrefixesCached( $local ) {
354  wfDebug( __METHOD__ . "()" );
355 
356  $wikiId = WikiMap::getCurrentWikiId();
357 
358  $data = [];
359  try {
360  /* Resolve site name */
361  if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
362  $site = $this->getCacheValue( '__sites:' . $wikiId );
363 
364  if ( $site == '' ) {
365  $this->thisSite = $this->fallbackSite;
366  } else {
367  $this->thisSite = $site;
368  }
369  }
370 
371  // List of interwiki sources
372  $sources = [];
373  // Global Level
374  if ( $this->interwikiScopes >= 2 ) {
375  $sources[] = '__global';
376  }
377  // Site level
378  if ( $this->interwikiScopes >= 3 ) {
379  $sources[] = '_' . $this->thisSite;
380  }
381  $sources[] = $wikiId;
382 
383  foreach ( $sources as $source ) {
384  $list = $this->getCacheValue( '__list:' . $source );
385  foreach ( explode( ' ', $list ) as $iw_prefix ) {
386  $row = $this->getCacheValue( "{$source}:{$iw_prefix}" );
387  if ( !$row ) {
388  continue;
389  }
390 
391  list( $iw_local, $iw_url ) = explode( ' ', $row );
392 
393  if ( $local !== null && $local != $iw_local ) {
394  continue;
395  }
396 
397  $data[$iw_prefix] = [
398  'iw_prefix' => $iw_prefix,
399  'iw_url' => $iw_url,
400  'iw_local' => $iw_local,
401  ];
402  }
403  }
404  } catch ( CdbException $e ) {
405  wfDebug( __METHOD__ . ": CdbException caught, error message was "
406  . $e->getMessage() );
407  }
408 
409  return array_values( $data );
410  }
411 
425  public static function buildCdbHash(
426  array $allPrefixes, int $scope = 1, ?string $thisSite = null
427  ): array {
428  $result = [];
429  $wikiId = WikiMap::getCurrentWikiId();
430  $keyPrefix = ( $scope >= 2 ) ? '__global' : $wikiId;
431  if ( $scope >= 3 && $thisSite ) {
432  $result[ "__sites:$wikiId" ] = $thisSite;
433  $keyPrefix = "_$thisSite";
434  }
435  $list = [];
436  foreach ( $allPrefixes as $iwInfo ) {
437  $prefix = $iwInfo['iw_prefix'];
438  $result["$keyPrefix:$prefix"] = implode( ' ', [
439  $iwInfo['iw_local'] ?? 0, $iwInfo['iw_url']
440  ] );
441  $list[] = $prefix;
442  }
443  $result["__list:$keyPrefix"] = implode( ' ', $list );
444  $result["__list:__sites"] = $wikiId;
445  return $result;
446  }
447 
454  private function getAllPrefixesDB( $local ) {
455  $db = $this->loadBalancer->getConnectionRef( DB_REPLICA );
456 
457  $where = [];
458 
459  if ( $local !== null ) {
460  if ( $local == 1 ) {
461  $where['iw_local'] = 1;
462  } elseif ( $local == 0 ) {
463  $where['iw_local'] = 0;
464  }
465  }
466 
467  $res = $db->select( 'interwiki',
468  self::selectFields(),
469  $where, __METHOD__, [ 'ORDER BY' => 'iw_prefix' ]
470  );
471 
472  $retval = [];
473  foreach ( $res as $row ) {
474  $retval[] = (array)$row;
475  }
476 
477  return $retval;
478  }
479 
486  public function getAllPrefixes( $local = null ) {
487  if ( $this->cdbData ) {
488  return $this->getAllPrefixesCached( $local );
489  }
490 
491  return $this->getAllPrefixesDB( $local );
492  }
493 
499  private static function selectFields() {
500  return [
501  'iw_prefix',
502  'iw_url',
503  'iw_api',
504  'iw_wikiid',
505  'iw_local',
506  'iw_trans'
507  ];
508  }
509 
510 }
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:353
MediaWiki\Interwiki\ClassicInterwikiLookup\resetLocalCache
resetLocalCache()
Resets locally cached Interwiki objects.
Definition: ClassicInterwikiLookup.php:185
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:425
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:454
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:5148
MediaWiki\Interwiki\ClassicInterwikiLookup\getInterwikiCacheEntry
getInterwikiCacheEntry( $prefix)
Get entry from interwiki cache.
Definition: ClassicInterwikiLookup.php:228
MediaWiki\Interwiki\ClassicInterwikiLookup\load
load( $prefix)
Load the interwiki, trying first memcached then the DB.
Definition: ClassicInterwikiLookup.php:287
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:37
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:486
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:128
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:193
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:499
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:555
MediaWiki\Interwiki\ClassicInterwikiLookup\getInterwikiCached
getInterwikiCached( $prefix)
Fetch interwiki prefix data from local cache in constant database.
Definition: ClassicInterwikiLookup.php:208
MediaWiki\Interwiki\ClassicInterwikiLookup\getCacheValue
getCacheValue( $key)
Definition: ClassicInterwikiLookup.php:263
MediaWiki\Interwiki\ClassicInterwikiLookup\loadFromArray
loadFromArray( $mc)
Fill in member variables from an array (e.g.
Definition: ClassicInterwikiLookup.php:333
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