*/
/**
- * This class provides access to the resource message blobs storage used
- * by ResourceLoader.
+ * This class provides access to the message blobs used by ResourceLoader modules.
*
* A message blob is a JSON object containing the interface messages for a
- * certain resource in a certain language. These message blobs are cached
- * in the msg_resource table and automatically invalidated when one of their
- * constituent messages or the resource itself is changed.
+ * certain module in a certain language. These message blobs are cached
+ * in the automatically invalidated when one of their constituent messages,
+ * or the module definition, is changed.
*/
class MessageBlobStore {
/**
*/
protected $blobCache = array();
+ /* @var ResourceLoader */
+ protected $resourceloader;
+
/**
- * Get the singleton instance
- *
- * @since 1.24
- * @deprecated since 1.25
- * @return MessageBlobStore
+ * @param ResourceLoader $resourceloader
*/
- public static function getInstance() {
- wfDeprecated( __METHOD__, '1.25' );
- return new self;
+ public function __construct( ResourceLoader $resourceloader = null ) {
+ $this->resourceloader = $resourceloader;
}
/**
if ( isset( $this->blobCache[$lang][$name] ) ) {
$blobs[$name] = $this->blobCache[$lang][$name];
} else {
- $missingFromCache[] = $name;
+ $missingFromCache[$name] = $module;
}
}
// Try DB cache
if ( $missingFromCache ) {
- $blobs += $this->getFromDB( $resourceLoader, $missingFromCache, $lang );
+ $blobs += $this->getFromDB( $missingFromCache, $lang );
}
// Generate new blobs for any remaining modules and store in DB
* @param string $name Module name
* @param ResourceLoaderModule $module
* @param string $lang Language code
- * @return mixed Message blob or false if the module has no messages
+ * @return string JSON blob
*/
public function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
$blob = $this->generateMessageBlob( $module, $lang );
array( 'IGNORE' )
);
- if ( $success ) {
- if ( $dbw->affectedRows() == 0 ) {
- // Blob was already present, fetch it
- $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
- 'mr_resource' => $name,
- 'mr_lang' => $lang,
- ),
- __METHOD__
- );
- } else {
- // Update msg_resource_links
- $rows = array();
-
- foreach ( $module->getMessages() as $key ) {
- $rows[] = array(
- 'mrl_resource' => $name,
- 'mrl_message' => $key
- );
- }
- $dbw->insert( 'msg_resource_links', $rows,
- __METHOD__, array( 'IGNORE' )
- );
- }
+ if ( $success && $dbw->affectedRows() == 0 ) {
+ // Blob was already present, fetch it
+ $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
+ 'mr_resource' => $name,
+ 'mr_lang' => $lang,
+ ),
+ __METHOD__
+ );
}
} catch ( DBError $e ) {
wfDebug( __METHOD__ . " failed to update DB: $e\n" );
return null;
}
- // Save the old and new blobs for later
- $oldBlob = $row->mr_blob;
$newBlob = $this->generateMessageBlob( $module, $lang );
try {
array( array( 'mr_resource', 'mr_lang' ) ),
$newRow, __METHOD__
);
-
- // Figure out which messages were added and removed
- $oldMessages = array_keys( FormatJson::decode( $oldBlob, true ) );
- $newMessages = array_keys( FormatJson::decode( $newBlob, true ) );
- $added = array_diff( $newMessages, $oldMessages );
- $removed = array_diff( $oldMessages, $newMessages );
-
- // Delete removed messages, insert added ones
- if ( $removed ) {
- $dbw->delete( 'msg_resource_links', array(
- 'mrl_resource' => $name,
- 'mrl_message' => $removed
- ), __METHOD__
- );
- }
-
- $newLinksRows = array();
-
- foreach ( $added as $message ) {
- $newLinksRows[] = array(
- 'mrl_resource' => $name,
- 'mrl_message' => $message
- );
- }
-
- if ( $newLinksRows ) {
- $dbw->insert( 'msg_resource_links', $newLinksRows, __METHOD__,
- array( 'IGNORE' ) // just in case
- );
- }
} catch ( Exception $e ) {
wfDebug( __METHOD__ . " failed to update DB: $e\n" );
}
}
} while ( count( $updates ) );
- // No need to update msg_resource_links because we didn't add
- // or remove any messages, we just changed their contents.
} catch ( Exception $e ) {
wfDebug( __METHOD__ . " failed to update DB: $e\n" );
}
}
public function clear() {
- // TODO: Give this some more thought
try {
// Not using TRUNCATE, because that needs extra permissions,
// which maybe not granted to the database user.
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'msg_resource', '*', __METHOD__ );
- $dbw->delete( 'msg_resource_links', '*', __METHOD__ );
} catch ( Exception $e ) {
wfDebug( __METHOD__ . " failed to update DB: $e\n" );
}
}
+ /**
+ * @return ResourceLoader
+ */
+ protected function getResourceLoader() {
+ // For back-compat this class supports instantiation without passing ResourceLoader
+ // Lazy-initialise this property because most callers don't need it.
+ if ( $this->resourceloader === null ) {
+ wfDebug( __CLASS__ . ' created without a ResourceLoader instance' );
+ $this->resourceloader = new ResourceLoader();
+ }
+
+ return $this->resourceloader;
+ }
+
/**
* Create an update queue for updateMessage()
*
$dbw = wfGetDB( DB_MASTER );
if ( is_null( $prevUpdates ) ) {
+ $rl = $this->getResourceLoader();
+ $moduleNames = $rl->getModulesByMessage( $key );
// Fetch all blobs referencing $key
$res = $dbw->select(
- array( 'msg_resource', 'msg_resource_links' ),
+ array( 'msg_resource' ),
array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
- array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
+ array(
+ 'mr_resource' => $moduleNames,
+ ),
__METHOD__
);
} else {
return $updates;
}
+ /**
+ * @param string $key Message key
+ * @param string $lang Language code
+ * @return string
+ */
+ private function fetchMessage( $key, $lang ) {
+ $message = wfMessage( $key )->inLanguage( $lang );
+ if ( !$message->exists() ) {
+ wfDebugLog( 'resourceloader', __METHOD__ . " failed to find: '$key' ($lang)" );
+ }
+ return $message->plain();
+ }
+
/**
* Reencode a message blob with the updated value for a message
*
*/
private function reencodeBlob( $blob, $key, $lang ) {
$decoded = FormatJson::decode( $blob, true );
- $decoded[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
-
+ $decoded[$key] = $this->fetchMessage( $key, $lang );
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 ResourceLoader $resourceLoader
- * @param array $modules Array of module names
+ * @param array $modules Array of module objects by name
* @param string $lang Language code
* @throws MWException
* @return array Array mapping module names to blobs
*/
- private function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) {
+ private function getFromDB( $modules, $lang ) {
if ( !count( $modules ) ) {
return array();
}
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'msg_resource',
array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
- array( 'mr_resource' => $modules, 'mr_lang' => $lang ),
+ array( 'mr_resource' => array_keys( $modules ), 'mr_lang' => $lang ),
__METHOD__
);
foreach ( $res as $row ) {
- $module = $resourceLoader->getModule( $row->mr_resource );
- if ( !$module ) {
+ if ( !isset( $modules[ $row->mr_resource ] ) ) {
// This shouldn't be possible
throw new MWException( __METHOD__ . ' passed an invalid module name' );
}
+ $module = $modules[ $row->mr_resource ];
// Update the module's blob if the list of messages changed
$blobKeys = array_keys( FormatJson::decode( $row->mr_blob, true ) );
*
* @param ResourceLoaderModule $module
* @param string $lang Language code
- * @return string JSON object
+ * @return string JSON blob
*/
private function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
$messages = array();
foreach ( $module->getMessages() as $key ) {
- $messages[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
+ $messages[$key] = $this->fetchMessage( $key, $lang );
}
return FormatJson::encode( (object)$messages );