MediaWiki REL1_37
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;
36
50
54 private $localCache;
55
59 private $contLang;
60
64 private $objectCache;
65
70
74 private $cdbData;
75
80
85
89 private $cdbReader = null;
90
94 private $thisSite = null;
95
97 private $hookRunner;
98
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}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
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:42
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.
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.
__construct(Language $contLang, WANObjectCache $objectCache, HookContainer $hookContainer, ILoadBalancer $loadBalancer, $objectCacheExpiry, $cdbData, $interwikiScopes, $fallbackSite)
loadFromArray( $mc)
Fill in member variables from an array (e.g.
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...
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:52
Service interface for looking up Interwiki records.
Database cluster connection, tracking, load balancing, and transaction manager interface.
$source
Copyright (C) 2018 Kunal Mehta legoktm@debian.org
const DB_REPLICA
Definition defines.php:25