MediaWiki REL1_31
MessageBlobStore.php
Go to the documentation of this file.
1<?php
26use Psr\Log\LoggerAwareInterface;
27use Psr\Log\LoggerInterface;
28use Psr\Log\NullLogger;
30
37class MessageBlobStore implements LoggerAwareInterface {
38
39 /* @var ResourceLoader|null */
41
45 protected $logger;
46
50 protected $wanCache;
51
56 public function __construct( ResourceLoader $rl = null, LoggerInterface $logger = null ) {
57 $this->resourceloader = $rl;
58 $this->logger = $logger ?: new NullLogger();
59 $this->wanCache = ObjectCache::getMainWANInstance();
60 }
61
66 public function setLogger( LoggerInterface $logger ) {
67 $this->logger = $logger;
68 }
69
78 public function getBlob( ResourceLoaderModule $module, $lang ) {
79 $blobs = $this->getBlobs( [ $module->getName() => $module ], $lang );
80 return $blobs[$module->getName()];
81 }
82
91 public function getBlobs( array $modules, $lang ) {
92 // Each cache key for a message blob by module name and language code also has a generic
93 // check key without language code. This is used to invalidate any and all language subkeys
94 // that exist for a module from the updateMessage() method.
96 $checkKeys = [
97 // Global check key, see clear()
98 $cache->makeKey( __CLASS__ )
99 ];
100 $cacheKeys = [];
101 foreach ( $modules as $name => $module ) {
102 $cacheKey = $this->makeCacheKey( $module, $lang );
103 $cacheKeys[$name] = $cacheKey;
104 // Per-module check key, see updateMessage()
105 $checkKeys[$cacheKey][] = $cache->makeKey( __CLASS__, $name );
106 }
107 $curTTLs = [];
108 $result = $cache->getMulti( array_values( $cacheKeys ), $curTTLs, $checkKeys );
109
110 $blobs = [];
111 foreach ( $modules as $name => $module ) {
112 $key = $cacheKeys[$name];
113 if ( !isset( $result[$key] ) || $curTTLs[$key] === null || $curTTLs[$key] < 0 ) {
114 $blobs[$name] = $this->recacheMessageBlob( $key, $module, $lang );
115 } else {
116 // Use unexpired cache
117 $blobs[$name] = $result[$key];
118 }
119 }
120 return $blobs;
121 }
122
127 public function get( ResourceLoader $resourceLoader, $modules, $lang ) {
128 return $this->getBlobs( $modules, $lang );
129 }
130
137 private function makeCacheKey( ResourceLoaderModule $module, $lang ) {
138 $messages = array_values( array_unique( $module->getMessages() ) );
139 sort( $messages );
140 return $this->wanCache->makeKey( __CLASS__, $module->getName(), $lang,
141 md5( json_encode( $messages ) )
142 );
143 }
144
152 protected function recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang ) {
153 $blob = $this->generateMessageBlob( $module, $lang );
155 $cache->set( $cacheKey, $blob,
156 // Add part of a day to TTL to avoid all modules expiring at once
157 $cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
158 Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
159 );
160 return $blob;
161 }
162
169 public function updateMessage( $key ) {
170 $moduleNames = $this->getResourceLoader()->getModulesByMessage( $key );
171 foreach ( $moduleNames as $moduleName ) {
172 // Uses a holdoff to account for database replica DB lag (for MessageCache)
173 $this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
174 }
175 }
176
181 public function clear() {
183 // Disable holdoff because this invalidates all modules and also not needed since
184 // LocalisationCache is stored outside the database and doesn't have lag.
185 $cache->touchCheckKey( $cache->makeKey( __CLASS__ ), $cache::HOLDOFF_NONE );
186 }
187
192 protected function getResourceLoader() {
193 // Back-compat: This class supports instantiation without a ResourceLoader object.
194 // Lazy-initialise this property because most callers don't need it.
195 if ( $this->resourceloader === null ) {
196 $this->logger->warning( __CLASS__ . ' created without a ResourceLoader instance' );
197 $this->resourceloader = new ResourceLoader();
198 }
200 }
201
208 protected function fetchMessage( $key, $lang ) {
209 $message = wfMessage( $key )->inLanguage( $lang );
210 $value = $message->plain();
211 if ( !$message->exists() ) {
212 $this->logger->warning( 'Failed to find {messageKey} ({lang})', [
213 'messageKey' => $key,
214 'lang' => $lang,
215 ] );
216 }
217 return $value;
218 }
219
227 private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
228 $messages = [];
229 foreach ( $module->getMessages() as $key ) {
230 $messages[$key] = $this->fetchMessage( $key, $lang );
231 }
232
233 $json = FormatJson::encode( (object)$messages );
234 // @codeCoverageIgnoreStart
235 if ( $json === false ) {
236 $this->logger->warning( 'Failed to encode message blob for {module} ({lang})', [
237 'module' => $module->getName(),
238 'lang' => $lang,
239 ] );
240 $json = '{}';
241 }
242 // codeCoverageIgnoreEnd
243 return $json;
244 }
245}
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
$messages
This class generates message blobs for use by ResourceLoader modules.
getBlobs(array $modules, $lang)
Get the message blobs for a set of modules.
fetchMessage( $key, $lang)
generateMessageBlob(ResourceLoaderModule $module, $lang)
Generate the message blob for a given module in a given language.
WANObjectCache $wanCache
recacheMessageBlob( $cacheKey, ResourceLoaderModule $module, $lang)
getBlob(ResourceLoaderModule $module, $lang)
Get the message blob for a module.
LoggerInterface $logger
__construct(ResourceLoader $rl=null, LoggerInterface $logger=null)
setLogger(LoggerInterface $logger)
clear()
Invalidate cache keys for all known modules.
updateMessage( $key)
Invalidate cache keys for modules using this message key.
makeCacheKey(ResourceLoaderModule $module, $lang)
Abstraction for ResourceLoader modules, with name registration and maxage functionality.
getMessages()
Get the messages needed for this module.
getName()
Get this module's name.
Dynamic JavaScript and CSS resource loading system.
Multi-datacenter aware caching interface.
touchCheckKey( $key, $holdoff=self::HOLDOFF_TTL)
Purge a "check" key from all datacenters, invalidating keys that use it.
set( $key, $value, $ttl=0, array $opts=[])
Set the value of a key in cache.
Relational database abstraction object.
Definition Database.php:48
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext such as when responding to a resource loader request or generating HTML output & $resourceLoader
Definition hooks.txt:2818
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:25
if(!isset( $args[0])) $lang