MediaWiki  master
MessageBlobStore.php
Go to the documentation of this file.
1 <?php
24 use Psr\Log\LoggerAwareInterface;
25 use Psr\Log\LoggerInterface;
26 use Psr\Log\NullLogger;
28 
38 class MessageBlobStore implements LoggerAwareInterface {
39 
41  private $resourceloader;
42 
46  protected $logger;
47 
51  protected $wanCache;
52 
58  public function __construct(
59  ResourceLoader $rl,
60  ?LoggerInterface $logger,
61  ?WANObjectCache $wanObjectCache
62  ) {
63  $this->resourceloader = $rl;
64  $this->logger = $logger ?: new NullLogger();
65 
66  // NOTE: when changing this assignment, make sure the code in the instantiator for
67  // LocalisationCache which calls MessageBlobStore::clearGlobalCacheEntry() uses the
68  // same cache object.
69  $this->wanCache = $wanObjectCache ?: MediaWikiServices::getInstance()
70  ->getMainWANObjectCache();
71  }
72 
77  public function setLogger( LoggerInterface $logger ) {
78  $this->logger = $logger;
79  }
80 
89  public function getBlob( ResourceLoaderModule $module, $lang ) {
90  $blobs = $this->getBlobs( [ $module->getName() => $module ], $lang );
91  return $blobs[$module->getName()];
92  }
93 
102  public function getBlobs( array $modules, $lang ) {
103  // Each cache key for a message blob by module name and language code also has a generic
104  // check key without language code. This is used to invalidate any and all language subkeys
105  // that exist for a module from the updateMessage() method.
107  $checkKeys = [
108  // Global check key, see clear()
109  $cache->makeGlobalKey( __CLASS__ )
110  ];
111  $cacheKeys = [];
112  foreach ( $modules as $name => $module ) {
113  $cacheKey = $this->makeCacheKey( $module, $lang );
114  $cacheKeys[$name] = $cacheKey;
115  // Per-module check key, see updateMessage()
116  $checkKeys[$cacheKey][] = $cache->makeKey( __CLASS__, $name );
117  }
118  $curTTLs = [];
119  $result = $cache->getMulti( array_values( $cacheKeys ), $curTTLs, $checkKeys );
120 
121  $blobs = [];
122  foreach ( $modules as $name => $module ) {
123  $key = $cacheKeys[$name];
124  if ( !isset( $result[$key] ) || $curTTLs[$key] === null || $curTTLs[$key] < 0 ) {
125  $blobs[$name] = $this->recacheMessageBlob( $key, $module, $lang );
126  } else {
127  // Use unexpired cache
128  $blobs[$name] = $result[$key];
129  }
130  }
131  return $blobs;
132  }
133 
140  private function makeCacheKey( ResourceLoaderModule $module, $lang ) {
141  $messages = array_values( array_unique( $module->getMessages() ) );
142  sort( $messages );
143  return $this->wanCache->makeKey( __CLASS__, $module->getName(), $lang,
144  md5( json_encode( $messages ) )
145  );
146  }
147 
155  protected function recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang ) {
156  $blob = $this->generateMessageBlob( $module, $lang );
158  $cache->set( $cacheKey, $blob,
159  // Add part of a day to TTL to avoid all modules expiring at once
160  $cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
161  Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
162  );
163  return $blob;
164  }
165 
172  public function updateMessage( $key ) {
173  $moduleNames = $this->getResourceLoader()->getModulesByMessage( $key );
174  foreach ( $moduleNames as $moduleName ) {
175  // Uses a holdoff to account for database replica DB lag (for MessageCache)
176  $this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
177  }
178  }
179 
183  public function clear() {
184  self::clearGlobalCacheEntry( $this->wanCache );
185  }
186 
194  public static function clearGlobalCacheEntry( WANObjectCache $cache ) {
195  // Disable hold-off because:
196  // - LocalisationCache is populated by messages on-disk and don't have DB lag,
197  // thus there is no need for hold off. We only clear it after new localisation
198  // updates are known to be deployed to all servers.
199  // - This global check key invalidates message blobs for all modules for all wikis
200  // in cache contexts (e.g. languages, skins). Setting a hold-off on this key could
201  // cause a cache stampede since no values would be stored for several seconds.
202  $cache->touchCheckKey( $cache->makeGlobalKey( __CLASS__ ), $cache::HOLDOFF_TTL_NONE );
203  }
204 
209  protected function getResourceLoader() {
210  return $this->resourceloader;
211  }
212 
219  protected function fetchMessage( $key, $lang ) {
220  $message = wfMessage( $key )->inLanguage( $lang );
221  if ( !$message->exists() ) {
222  $this->logger->warning( 'Failed to find {messageKey} ({lang})', [
223  'messageKey' => $key,
224  'lang' => $lang,
225  ] );
226  $value = null;
227  } else {
228  $value = $message->plain();
229  }
230  return $value;
231  }
232 
240  private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
241  $messages = [];
242  foreach ( $module->getMessages() as $key ) {
243  $value = $this->fetchMessage( $key, $lang );
244  if ( $value !== null ) {
245  $messages[$key] = $value;
246  }
247  }
248 
249  $json = FormatJson::encode( (object)$messages, false, FormatJson::UTF8_OK );
250  // @codeCoverageIgnoreStart
251  if ( $json === false ) {
252  $this->logger->warning( 'Failed to encode message blob for {module} ({lang})', [
253  'module' => $module->getName(),
254  'lang' => $lang,
255  ] );
256  $json = '{}';
257  }
258  // codeCoverageIgnoreEnd
259  return $json;
260  }
261 }
MessageBlobStore\getResourceLoader
getResourceLoader()
Definition: MessageBlobStore.php:209
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:49
MessageBlobStore\clearGlobalCacheEntry
static clearGlobalCacheEntry(WANObjectCache $cache)
Invalidate cache keys for all known modules.
Definition: MessageBlobStore.php:194
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:146
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
MessageBlobStore\fetchMessage
fetchMessage( $key, $lang)
Definition: MessageBlobStore.php:219
MessageBlobStore\clear
clear()
Invalidate cache keys for all known modules.
Definition: MessageBlobStore.php:183
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
FormatJson\UTF8_OK
const UTF8_OK
Skip escaping most characters above U+007F for readability and compactness.
Definition: FormatJson.php:34
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:115
MessageBlobStore\updateMessage
updateMessage( $key)
Invalidate cache keys for modules using this message key.
Definition: MessageBlobStore.php:172
$blob
$blob
Definition: testCompression.php:70
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2463
$modules
$modules
Definition: HTMLFormElement.php:13
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MessageBlobStore\$logger
LoggerInterface $logger
Definition: MessageBlobStore.php:46
MessageBlobStore\setLogger
setLogger(LoggerInterface $logger)
Definition: MessageBlobStore.php:77
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:120
ResourceLoaderModule\getMessages
getMessages()
Get the messages needed for this module.
Definition: ResourceLoaderModule.php:373
MessageBlobStore\makeCacheKey
makeCacheKey(ResourceLoaderModule $module, $lang)
Definition: MessageBlobStore.php:140
ResourceLoaderModule
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
Definition: ResourceLoaderModule.php:38
ResourceLoader
ResourceLoader is a loading system for JavaScript and CSS resources.
Definition: ResourceLoader.php:56
$cache
$cache
Definition: mcc.php:33
MessageBlobStore\$resourceloader
ResourceLoader $resourceloader
Definition: MessageBlobStore.php:41
MessageBlobStore\generateMessageBlob
generateMessageBlob(ResourceLoaderModule $module, $lang)
Generate the message blob for a given module in a given language.
Definition: MessageBlobStore.php:240
MessageBlobStore\getBlobs
getBlobs(array $modules, $lang)
Get the message blobs for a set of modules.
Definition: MessageBlobStore.php:102
MessageBlobStore
This class generates message blobs for use by ResourceLoader.
Definition: MessageBlobStore.php:38
MessageBlobStore\$wanCache
WANObjectCache $wanCache
Definition: MessageBlobStore.php:51
MessageBlobStore\getBlob
getBlob(ResourceLoaderModule $module, $lang)
Get the message blob for a module.
Definition: MessageBlobStore.php:89
ResourceLoaderModule\getName
getName()
Get this module's name.
Definition: ResourceLoaderModule.php:118
MessageBlobStore\__construct
__construct(ResourceLoader $rl, ?LoggerInterface $logger, ?WANObjectCache $wanObjectCache)
Definition: MessageBlobStore.php:58
MessageBlobStore\recacheMessageBlob
recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang)
Definition: MessageBlobStore.php:155