* consistuent messages or the resource itself is changed.
*/
class MessageBlobStore {
+
/**
* Get the message blobs for a set of modules
- * @param $modules array Array of module names
+ *
+ * @param $resourceLoader ResourceLoader object
+ * @param $modules array Array of module objects keyed by module name
* @param $lang string Language code
* @return array An array mapping module names to message blobs
*/
- public static function get( $modules, $lang ) {
- // TODO: Invalidate blob when module touched
+ public static function get( ResourceLoader $resourceLoader, $modules, $lang ) {
+ wfProfileIn( __METHOD__ );
if ( !count( $modules ) ) {
+ wfProfileOut( __METHOD__ );
return array();
}
// Try getting from the DB first
- $blobs = self::getFromDB( $modules, $lang );
+ $blobs = self::getFromDB( $resourceLoader, array_keys( $modules ), $lang );
// Generate blobs for any missing modules and store them in the DB
- $missing = array_diff( $modules, array_keys( $blobs ) );
- foreach ( $missing as $module ) {
- $blob = self::insertMessageBlob( $module, $lang );
+ $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) );
+ foreach ( $missing as $name ) {
+ $blob = self::insertMessageBlob( $name, $modules[$name], $lang );
if ( $blob ) {
- $blobs[$module] = $blob;
+ $blobs[$name] = $blob;
}
}
+ wfProfileOut( __METHOD__ );
return $blobs;
}
* Generate and insert a new message blob. If the blob was already
* present, it is not regenerated; instead, the preexisting blob
* is fetched and returned.
- * @param $module string Module name
- * @param $lang string Language code
+ *
+ * @param $name String: module name
+ * @param $module ResourceLoaderModule object
+ * @param $lang String: language code
* @return mixed Message blob or false if the module has no messages
*/
- public static function insertMessageBlob( $module, $lang ) {
+ public static function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
$blob = self::generateMessageBlob( $module, $lang );
if ( !$blob ) {
return false;
}
- $dbw = wfGetDb( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$success = $dbw->insert( 'msg_resource', array(
'mr_lang' => $lang,
- 'mr_resource' => $module,
+ 'mr_resource' => $name,
'mr_blob' => $blob,
'mr_timestamp' => $dbw->timestamp()
),
if ( $success ) {
if ( $dbw->affectedRows() == 0 ) {
// Blob was already present, fetch it
- $dbr = wfGetDB( DB_SLAVE );
- $blob = $dbr->selectField( 'msg_resource', 'mr_blob', array(
- 'mr_resource' => $module,
+ $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
+ 'mr_resource' => $name,
'mr_lang' => $lang,
),
__METHOD__
} else {
// Update msg_resource_links
$rows = array();
- $mod = ResourceLoader::getModule( $module );
- foreach ( $mod->getMessages() as $key ) {
+ foreach ( $module->getMessages() as $key ) {
$rows[] = array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $key
);
}
/**
* Update all message blobs for a given module.
- * @param $module string Module name
- * @param $lang string Language code (optional)
- * @return mixed If $lang is set, the new message blob for that language is returned if present. Otherwise, null is returned.
+ *
+ * @param $name String: module name
+ * @param $module ResourceLoaderModule object
+ * @param $lang String: language code (optional)
+ * @return Mixed: if $lang is set, the new message blob for that language is
+ * returned if present. Otherwise, null is returned.
*/
- public static function updateModule( $module, $lang = null ) {
+ public static function updateModule( $name, ResourceLoaderModule $module, $lang = null ) {
$retval = null;
// Find all existing blobs for this module
- $dbw = wfGetDb( DB_MASTER );
- $res = $dbw->select( 'msg_resource', array( 'mr_lang', 'mr_blob' ),
- array( 'mr_resource' => $module ),
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->select( 'msg_resource',
+ array( 'mr_lang', 'mr_blob' ),
+ array( 'mr_resource' => $name ),
__METHOD__
);
$retval = $newBlob;
}
$newRows[] = array(
- 'mr_resource' => $module,
+ 'mr_resource' => $name,
'mr_lang' => $row->mr_lang,
'mr_blob' => $newBlob,
'mr_timestamp' => $now
// Delete removed messages, insert added ones
if ( $removed ) {
$dbw->delete( 'msg_resource_links', array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $removed
), __METHOD__
);
foreach ( $added as $message ) {
$newLinksRows[] = array(
- 'mrl_resource' => $module,
+ 'mrl_resource' => $name,
'mrl_message' => $message
);
}
/**
* Update a single message in all message blobs it occurs in.
- * @param $key string Message key
+ *
+ * @param $key String: message key
*/
public static function updateMessage( $key ) {
- $dbw = wfGetDb( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
// Keep running until the updates queue is empty.
// Due to update conflicts, the queue might not be emptied
// didn't change since we fetched it by putting
// the timestamp in the WHERE clause.
$success = $dbw->update( 'msg_resource',
- array( 'mr_blob' => $update['newBlob'],
+ array(
+ 'mr_blob' => $update['newBlob'],
'mr_timestamp' => $dbw->timestamp() ),
- array( 'mr_resource' => $update['resource'],
+ array(
+ 'mr_resource' => $update['resource'],
'mr_lang' => $update['lang'],
'mr_timestamp' => $update['timestamp'] ),
__METHOD__
public static function clear() {
// TODO: Give this some more thought
// TODO: Is TRUNCATE better?
- $dbw = wfGetDb( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'msg_resource', '*', __METHOD__ );
$dbw->delete( 'msg_resource_links', '*', __METHOD__ );
}
/**
* Create an update queue for updateMessage()
- * @param $key string Message key
- * @param $prevUpdates array Updates queue to refresh or null to build a fresh update queue
- * @return array Updates queue
+ *
+ * @param $key String: message key
+ * @param $prevUpdates Array: updates queue to refresh or null to build a fresh update queue
+ * @return Array: updates queue
*/
private static function getUpdatesForMessage( $key, $prevUpdates = null ) {
- $dbw = wfGetDb( DB_MASTER );
+ $dbw = wfGetDB( DB_MASTER );
if ( is_null( $prevUpdates ) ) {
// Fetch all blobs referencing $key
$res = $dbw->select(
- array( 'msg_resource', 'msg_resource_links' ),
- array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
- array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
+ array( 'msg_resource', 'msg_resource_links' ),
+ array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
+ array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
__METHOD__
);
} else {
}
$res = $dbw->select( 'msg_resource',
- array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
+ array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
$dbw->makeWhereFrom2d( $twoD, 'mr_resource', 'mr_lang' ),
__METHOD__
);
'resource' => $row->mr_resource,
'lang' => $row->mr_lang,
'timestamp' => $row->mr_timestamp,
- 'newBlob' => self::reencodeBlob( $row->mr_blob,
- $key, $row->mr_lang )
+ 'newBlob' => self::reencodeBlob( $row->mr_blob, $key, $row->mr_lang )
);
}
/**
* Reencode a message blob with the updated value for a message
- * @param $blob string Message blob (JSON object)
- * @param $key string Message key
- * @param $lang string Language code
+ *
+ * @param $blob String: message blob (JSON object)
+ * @param $key String: message key
+ * @param $lang String: language code
* @return Message blob with $key replaced with its new value
*/
private static function reencodeBlob( $blob, $key, $lang ) {
$decoded = FormatJson::decode( $blob, true );
$decoded[$key] = wfMsgExt( $key, array( 'language' => $lang ) );
- return FormatJson::encode( $decoded );
+ return FormatJson::encode( (object)$decoded );
}
/**
* Get the message blobs for a set of modules from the database.
* Modules whose blobs are not in the database are silently dropped.
- * @param $modules array Array of module names
- * @param $lang string Language code
+ *
+ * @param $resourceLoader ResourceLoader object
+ * @param $modules Array of module names
+ * @param $lang String: language code
* @return array Array mapping module names to blobs
*/
- private static function getFromDB( $modules, $lang ) {
+ private static function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) {
+ global $wgCacheEpoch;
$retval = array();
$dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( 'msg_resource', array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
+ $res = $dbr->select( 'msg_resource',
+ array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
array( 'mr_resource' => $modules, 'mr_lang' => $lang ),
__METHOD__
);
foreach ( $res as $row ) {
- $module = ResourceLoader::getModule( $row->mr_resource );
+ $module = $resourceLoader->getModule( $row->mr_resource );
if ( !$module ) {
// This shouldn't be possible
throw new MWException( __METHOD__ . ' passed an invalid module name' );
}
- if ( array_keys( FormatJson::decode( $row->mr_blob, true ) ) !== $module->getMessages() ) {
- $retval[$row->mr_resource] = self::updateModule( $row->mr_resource, $lang );
+ // Update the module's blobs if the set of messages changed or if the blob is
+ // older than $wgCacheEpoch
+ if ( array_keys( FormatJson::decode( $row->mr_blob, true ) ) !== $module->getMessages() ||
+ wfTimestamp( TS_MW, $row->mr_timestamp ) <= $wgCacheEpoch ) {
+ $retval[$row->mr_resource] = self::updateModule( $row->mr_resource, $module, $lang );
} else {
$retval[$row->mr_resource] = $row->mr_blob;
}
/**
* Generate the message blob for a given module in a given language.
- * @param $module string Module name
- * @param $lang string Language code
- * @return string JSON object
+ *
+ * @param $module ResourceLoaderModule object
+ * @param $lang String: language code
+ * @return String: JSON object
*/
- private static function generateMessageBlob( $module, $lang ) {
- $mod = ResourceLoader::getModule( $module );
+ private static function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
$messages = array();
- foreach ( $mod->getMessages() as $key ) {
+ foreach ( $module->getMessages() as $key ) {
$messages[$key] = wfMsgExt( $key, array( 'language' => $lang ) );
}
- return FormatJson::encode( $messages );
+ return FormatJson::encode( (object)$messages );
}
}