MediaWiki REL1_35
ClassicInterwikiLookup.php
Go to the documentation of this file.
1<?php
24
25use Cdb\Exception as CdbException;
26use Cdb\Reader as CdbReader;
27use Interwiki;
28use Language;
29use MapCacheLRU;
33use WikiMap;
35
49
53 private $localCache;
54
58 private $contLang;
59
63 private $objectCache;
64
69
73 private $cdbData;
74
79
84
88 private $cdbReader = null;
89
93 private $thisSite = null;
94
96 private $hookRunner;
97
112 public function __construct(
115 HookContainer $hookContainer,
117 $cdbData,
120 ) {
121 $this->localCache = new MapCacheLRU( 100 );
122
123 $this->contLang = $contLang;
124 $this->objectCache = $objectCache;
125 $this->hookRunner = new HookRunner( $hookContainer );
126 $this->objectCacheExpiry = $objectCacheExpiry;
127 $this->cdbData = $cdbData;
128 $this->interwikiScopes = $interwikiScopes;
129 $this->fallbackSite = $fallbackSite;
130 }
131
138 public function isValidInterwiki( $prefix ) {
139 $result = $this->fetch( $prefix );
140
141 return (bool)$result;
142 }
143
150 public function fetch( $prefix ) {
151 if ( $prefix == '' ) {
152 return null;
153 }
154
155 $prefix = $this->contLang->lc( $prefix );
156 if ( $this->localCache->has( $prefix ) ) {
157 return $this->localCache->get( $prefix );
158 }
159
160 if ( $this->cdbData ) {
161 $iw = $this->getInterwikiCached( $prefix );
162 } else {
163 $iw = $this->load( $prefix );
164 if ( !$iw ) {
165 $iw = false;
166 }
167 }
168 $this->localCache->set( $prefix, $iw );
169
170 return $iw;
171 }
172
178 public function resetLocalCache() {
179 $this->localCache->clear();
180 }
181
186 public function invalidateCache( $prefix ) {
187 $this->localCache->clear( $prefix );
188
189 $key = $this->objectCache->makeKey( 'interwiki', $prefix );
190 $this->objectCache->delete( $key );
191 }
192
201 private function getInterwikiCached( $prefix ) {
202 $value = $this->getInterwikiCacheEntry( $prefix );
203
204 if ( $value ) {
205 // Split values
206 list( $local, $url ) = explode( ' ', $value, 2 );
207 return new Interwiki( $prefix, $url, '', '', (int)$local );
208 } else {
209 return false;
210 }
211 }
212
221 private function getInterwikiCacheEntry( $prefix ) {
222 wfDebug( __METHOD__ . "( $prefix )" );
223
224 $wikiId = WikiMap::getCurrentWikiId();
225
226 $value = false;
227 try {
228 // Resolve site name
229 if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
230 $this->thisSite = $this->getCacheValue( '__sites:' . $wikiId );
231 if ( $this->thisSite == '' ) {
232 $this->thisSite = $this->fallbackSite;
233 }
234 }
235
236 $value = $this->getCacheValue( $wikiId . ':' . $prefix );
237 // Site level
238 if ( $value == '' && $this->interwikiScopes >= 3 ) {
239 $value = $this->getCacheValue( "_{$this->thisSite}:{$prefix}" );
240 }
241 // Global Level
242 if ( $value == '' && $this->interwikiScopes >= 2 ) {
243 $value = $this->getCacheValue( "__global:{$prefix}" );
244 }
245 if ( $value == 'undef' ) {
246 $value = '';
247 }
248 } catch ( CdbException $e ) {
249 wfDebug( __METHOD__ . ": CdbException caught, error message was "
250 . $e->getMessage() );
251 }
252
253 return $value;
254 }
255
256 private function getCacheValue( $key ) {
257 if ( $this->cdbReader === null ) {
258 if ( is_string( $this->cdbData ) ) {
259 $this->cdbReader = \Cdb\Reader::open( $this->cdbData );
260 } elseif ( is_array( $this->cdbData ) ) {
261 $this->cdbReader = new \Cdb\Reader\Hash( $this->cdbData );
262 } else {
263 $this->cdbReader = false;
264 }
265 }
266
267 if ( $this->cdbReader ) {
268 return $this->cdbReader->get( $key );
269 } else {
270 return false;
271 }
272 }
273
280 private function load( $prefix ) {
281 $iwData = [];
282 if ( !$this->hookRunner->onInterwikiLoadPrefix( $prefix, $iwData ) ) {
283 return $this->loadFromArray( $iwData );
284 }
285
286 if ( is_array( $iwData ) ) {
287 $iw = $this->loadFromArray( $iwData );
288 if ( $iw ) {
289 return $iw; // handled by hook
290 }
291 }
292
293 $fname = __METHOD__;
294 $iwData = $this->objectCache->getWithSetCallback(
295 $this->objectCache->makeKey( 'interwiki', $prefix ),
296 $this->objectCacheExpiry,
297 function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
298 $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
299
300 $setOpts += Database::getCacheSetOptions( $dbr );
301
302 $row = $dbr->selectRow(
303 'interwiki',
304 self::selectFields(),
305 [ 'iw_prefix' => $prefix ],
306 $fname
307 );
308
309 return $row ? (array)$row : '!NONEXISTENT';
310 }
311 );
312
313 if ( is_array( $iwData ) ) {
314 return $this->loadFromArray( $iwData ) ?: false;
315 }
316
317 return false;
318 }
319
326 private function loadFromArray( $mc ) {
327 if ( isset( $mc['iw_url'] ) ) {
328 $url = $mc['iw_url'];
329 $local = $mc['iw_local'] ?? 0;
330 $trans = $mc['iw_trans'] ?? 0;
331 $api = $mc['iw_api'] ?? '';
332 $wikiId = $mc['iw_wikiid'] ?? '';
333
334 return new Interwiki( null, $url, $api, $wikiId, $local, $trans );
335 }
336
337 return false;
338 }
339
346 private function getAllPrefixesCached( $local ) {
347 wfDebug( __METHOD__ . "()" );
348
349 $wikiId = WikiMap::getCurrentWikiId();
350
351 $data = [];
352 try {
353 /* Resolve site name */
354 if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
355 $site = $this->getCacheValue( '__sites:' . $wikiId );
356
357 if ( $site == '' ) {
358 $this->thisSite = $this->fallbackSite;
359 } else {
360 $this->thisSite = $site;
361 }
362 }
363
364 // List of interwiki sources
365 $sources = [];
366 // Global Level
367 if ( $this->interwikiScopes >= 2 ) {
368 $sources[] = '__global';
369 }
370 // Site level
371 if ( $this->interwikiScopes >= 3 ) {
372 $sources[] = '_' . $this->thisSite;
373 }
374 $sources[] = $wikiId;
375
376 foreach ( $sources as $source ) {
377 $list = $this->getCacheValue( '__list:' . $source );
378 foreach ( explode( ' ', $list ) as $iw_prefix ) {
379 $row = $this->getCacheValue( "{$source}:{$iw_prefix}" );
380 if ( !$row ) {
381 continue;
382 }
383
384 list( $iw_local, $iw_url ) = explode( ' ', $row );
385
386 if ( $local !== null && $local != $iw_local ) {
387 continue;
388 }
389
390 $data[$iw_prefix] = [
391 'iw_prefix' => $iw_prefix,
392 'iw_url' => $iw_url,
393 'iw_local' => $iw_local,
394 ];
395 }
396 }
397 } catch ( CdbException $e ) {
398 wfDebug( __METHOD__ . ": CdbException caught, error message was "
399 . $e->getMessage() );
400 }
401
402 return array_values( $data );
403 }
404
411 private function getAllPrefixesDB( $local ) {
412 $db = wfGetDB( DB_REPLICA ); // TODO: inject DB LoadBalancer
413
414 $where = [];
415
416 if ( $local !== null ) {
417 if ( $local == 1 ) {
418 $where['iw_local'] = 1;
419 } elseif ( $local == 0 ) {
420 $where['iw_local'] = 0;
421 }
422 }
423
424 $res = $db->select( 'interwiki',
425 self::selectFields(),
426 $where, __METHOD__, [ 'ORDER BY' => 'iw_prefix' ]
427 );
428
429 $retval = [];
430 foreach ( $res as $row ) {
431 $retval[] = (array)$row;
432 }
433
434 return $retval;
435 }
436
443 public function getAllPrefixes( $local = null ) {
444 if ( $this->cdbData ) {
445 return $this->getAllPrefixesCached( $local );
446 }
447
448 return $this->getAllPrefixesDB( $local );
449 }
450
456 private static function selectFields() {
457 return [
458 'iw_prefix',
459 'iw_url',
460 'iw_api',
461 'iw_wikiid',
462 'iw_local',
463 'iw_trans'
464 ];
465 }
466
467}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Value object for representing interwiki records.
Definition Interwiki.php:26
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition Language.php:41
Handles a simple LRU key/value map with a maximum number of entries.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
InterwikiLookup implementing the "classic" interwiki storage (hardcoded up to MW 1....
getAllPrefixesDB( $local)
Fetch all interwiki prefixes from DB.
load( $prefix)
Load the interwiki, trying first memcached then the DB.
fetch( $prefix)
Fetch an Interwiki object.
__construct(Language $contLang, WANObjectCache $objectCache, HookContainer $hookContainer, $objectCacheExpiry, $cdbData, $interwikiScopes, $fallbackSite)
invalidateCache( $prefix)
Purge the in-process and object cache for an interwiki prefix.
resetLocalCache()
Resets locally cached Interwiki objects.
static selectFields()
Return the list of interwiki fields that should be selected to create a new Interwiki object.
getAllPrefixes( $local=null)
Returns all interwiki prefixes.
isValidInterwiki( $prefix)
Check whether an interwiki prefix exists.
loadFromArray( $mc)
Fill in member variables from an array (e.g.
getInterwikiCacheEntry( $prefix)
Get entry from interwiki cache.
getAllPrefixesCached( $local)
Fetch all interwiki prefixes from interwiki cache.
getInterwikiCached( $prefix)
Fetch interwiki prefix data from local cache in constant database.
Multi-datacenter aware caching interface.
Helper tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:29
Relational database abstraction object.
Definition Database.php:50
Service interface for looking up Interwiki records.
$source
Copyright (C) 2018 Kunal Mehta legoktm@member.fsf.org
const DB_REPLICA
Definition defines.php:25