MediaWiki REL1_39
ClassicInterwikiLookup.php
Go to the documentation of this file.
1<?php
24
25use Interwiki;
26use Language;
27use MapCacheLRU;
30use MWException;
32use WikiMap;
35
49
53 private $localCache;
54
58 private $contLang;
59
63 private $objectCache;
64
68 private $objectCacheExpiry;
69
73 private $data;
74
78 private $interwikiScopes;
79
83 private $fallbackSite;
84
88 private $thisSite = null;
89
91 private $hookRunner;
92
94 private $loadBalancer;
95
110 public function __construct(
111 Language $contLang,
112 WANObjectCache $objectCache,
113 HookContainer $hookContainer,
114 ILoadBalancer $loadBalancer,
115 $objectCacheExpiry,
116 $interwikiData,
117 $interwikiScopes,
118 $fallbackSite
119 ) {
120 $this->localCache = new MapCacheLRU( 1000 );
121
122 $this->contLang = $contLang;
123 $this->objectCache = $objectCache;
124 $this->hookRunner = new HookRunner( $hookContainer );
125 $this->loadBalancer = $loadBalancer;
126 $this->objectCacheExpiry = $objectCacheExpiry;
127 if ( is_array( $interwikiData ) ) {
128 $this->data = $interwikiData;
129 } elseif ( $interwikiData ) {
130 throw new MWException(
131 'Setting $wgInterwikiCache to a CDB path is no longer supported' );
132 }
133 $this->interwikiScopes = $interwikiScopes;
134 $this->fallbackSite = $fallbackSite;
135 }
136
143 public function isValidInterwiki( $prefix ) {
144 $result = $this->fetch( $prefix );
145
146 return (bool)$result;
147 }
148
155 public function fetch( $prefix ) {
156 if ( $prefix == '' ) {
157 return null;
158 }
159
160 $prefix = $this->contLang->lc( $prefix );
161
162 return $this->localCache->getWithSetCallback(
163 $prefix,
164 function () use ( $prefix ) {
165 if ( $this->data !== null ) {
166 $iw = $this->fetchPregenerated( $prefix );
167 } else {
168 $iw = $this->load( $prefix );
169 if ( !$iw ) {
170 $iw = false;
171 }
172 }
173 return $iw;
174 }
175 );
176 }
177
183 public function resetLocalCache() {
184 $this->localCache->clear();
185 }
186
191 public function invalidateCache( $prefix ) {
192 $this->localCache->clear( $prefix );
193
194 $key = $this->objectCache->makeKey( 'interwiki', $prefix );
195 $this->objectCache->delete( $key );
196 }
197
206 private function fetchPregenerated( $prefix ) {
207 $value = $this->getPregeneratedEntry( $prefix );
208
209 if ( $value ) {
210 // Split values
211 list( $local, $url ) = explode( ' ', $value, 2 );
212 return new Interwiki( $prefix, $url, '', '', (int)$local );
213 } else {
214 return false;
215 }
216 }
217
226 private function getPregeneratedEntry( $prefix ) {
227 wfDebug( __METHOD__ . "( $prefix )" );
228
229 $wikiId = WikiMap::getCurrentWikiId();
230
231 // Resolve site name
232 if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
233 $this->thisSite = $this->data['__sites:' . $wikiId] ?? $this->fallbackSite;
234 }
235
236 $value = $this->data[$wikiId . ':' . $prefix] ?? false;
237 // Site level
238 if ( $value === false && $this->interwikiScopes >= 3 ) {
239 $value = $this->data["_{$this->thisSite}:{$prefix}"] ?? false;
240 }
241 // Global Level
242 if ( $value === false && $this->interwikiScopes >= 2 ) {
243 $value = $this->data["__global:{$prefix}"] ?? false;
244 }
245
246 return $value;
247 }
248
255 private function load( $prefix ) {
256 $iwData = [];
257 if ( !$this->hookRunner->onInterwikiLoadPrefix( $prefix, $iwData ) ) {
258 return $this->loadFromArray( $iwData );
259 }
260
261 if ( is_array( $iwData ) ) {
262 $iw = $this->loadFromArray( $iwData );
263 if ( $iw ) {
264 return $iw; // handled by hook
265 }
266 }
267
268 $fname = __METHOD__;
269 $iwData = $this->objectCache->getWithSetCallback(
270 $this->objectCache->makeKey( 'interwiki', $prefix ),
271 $this->objectCacheExpiry,
272 function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix, $fname ) {
273 $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
274
275 $setOpts += Database::getCacheSetOptions( $dbr );
276
277 $row = $dbr->selectRow(
278 'interwiki',
279 self::selectFields(),
280 [ 'iw_prefix' => $prefix ],
281 $fname
282 );
283
284 return $row ? (array)$row : '!NONEXISTENT';
285 }
286 );
287
288 if ( is_array( $iwData ) ) {
289 return $this->loadFromArray( $iwData ) ?: false;
290 }
291
292 return false;
293 }
294
301 private function loadFromArray( $mc ) {
302 if ( isset( $mc['iw_url'] ) ) {
303 $url = $mc['iw_url'];
304 $local = $mc['iw_local'] ?? 0;
305 $trans = $mc['iw_trans'] ?? 0;
306 $api = $mc['iw_api'] ?? '';
307 $wikiId = $mc['iw_wikiid'] ?? '';
308
309 return new Interwiki( null, $url, $api, $wikiId, $local, $trans );
310 }
311
312 return false;
313 }
314
321 private function getAllPrefixesPregenerated( $local ) {
322 wfDebug( __METHOD__ . "()" );
323
324 $wikiId = WikiMap::getCurrentWikiId();
325
326 $data = [];
327 /* Resolve site name */
328 if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
329 $this->thisSite = $this->data['__sites:' . $wikiId] ?? $this->fallbackSite;
330 }
331
332 // List of interwiki sources
333 $sources = [];
334 // Global Level
335 if ( $this->interwikiScopes >= 2 ) {
336 $sources[] = '__global';
337 }
338 // Site level
339 if ( $this->interwikiScopes >= 3 ) {
340 $sources[] = '_' . $this->thisSite;
341 }
342 $sources[] = $wikiId;
343
344 foreach ( $sources as $source ) {
345 $list = $this->data['__list:' . $source] ?? '';
346 foreach ( explode( ' ', $list ) as $iw_prefix ) {
347 $row = $this->data["{$source}:{$iw_prefix}"] ?? null;
348 if ( !$row ) {
349 continue;
350 }
351
352 list( $iw_local, $iw_url ) = explode( ' ', $row );
353
354 if ( $local !== null && $local != $iw_local ) {
355 continue;
356 }
357
358 $data[$iw_prefix] = [
359 'iw_prefix' => $iw_prefix,
360 'iw_url' => $iw_url,
361 'iw_local' => $iw_local,
362 ];
363 }
364 }
365
366 return array_values( $data );
367 }
368
382 public static function buildCdbHash(
383 array $allPrefixes, int $scope = 1, ?string $thisSite = null
384 ): array {
385 $result = [];
386 $wikiId = WikiMap::getCurrentWikiId();
387 $keyPrefix = ( $scope >= 2 ) ? '__global' : $wikiId;
388 if ( $scope >= 3 && $thisSite ) {
389 $result[ "__sites:$wikiId" ] = $thisSite;
390 $keyPrefix = "_$thisSite";
391 }
392 $list = [];
393 foreach ( $allPrefixes as $iwInfo ) {
394 $prefix = $iwInfo['iw_prefix'];
395 $result["$keyPrefix:$prefix"] = implode( ' ', [
396 $iwInfo['iw_local'] ?? 0, $iwInfo['iw_url']
397 ] );
398 $list[] = $prefix;
399 }
400 $result["__list:$keyPrefix"] = implode( ' ', $list );
401 $result["__list:__sites"] = $wikiId;
402 return $result;
403 }
404
411 private function getAllPrefixesDB( $local ) {
412 $db = $this->loadBalancer->getConnectionRef( DB_REPLICA );
413
414 $where = [];
415
416 if ( $local !== null ) {
417 $where['iw_local'] = (int)$local;
418 }
419
420 $res = $db->select( 'interwiki',
421 self::selectFields(),
422 $where, __METHOD__, [ 'ORDER BY' => 'iw_prefix' ]
423 );
424
425 $retval = [];
426 foreach ( $res as $row ) {
427 $retval[] = (array)$row;
428 }
429
430 return $retval;
431 }
432
439 public function getAllPrefixes( $local = null ) {
440 if ( $this->data !== null ) {
441 return $this->getAllPrefixesPregenerated( $local );
442 }
443
444 return $this->getAllPrefixesDB( $local );
445 }
446
452 private static function selectFields() {
453 return [
454 'iw_prefix',
455 'iw_url',
456 'iw_api',
457 'iw_wikiid',
458 'iw_local',
459 'iw_trans'
460 ];
461 }
462
463}
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
Base class for language-specific code.
Definition Language.php:53
MediaWiki exception.
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....
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.
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, $interwikiData, $interwikiScopes, $fallbackSite)
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 self::__construc...
Multi-datacenter aware caching interface.
Helper tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:29
static getCurrentWikiId()
Definition WikiMap.php:303
static getCacheSetOptions(?IDatabase ... $dbs)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Service interface for looking up Interwiki records.
Create and track the database connections and transactions for a given database cluster.
$source
Copyright (C) 2018 Kunal Mehta legoktm@debian.org
const DB_REPLICA
Definition defines.php:26