00001 <?php
00034 class MessageBlobStore {
00035
00044 public static function get( ResourceLoader $resourceLoader, $modules, $lang ) {
00045 wfProfileIn( __METHOD__ );
00046 if ( !count( $modules ) ) {
00047 wfProfileOut( __METHOD__ );
00048 return array();
00049 }
00050
00051 $blobs = self::getFromDB( $resourceLoader, array_keys( $modules ), $lang );
00052
00053
00054 $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) );
00055 foreach ( $missing as $name ) {
00056 $blob = self::insertMessageBlob( $name, $modules[$name], $lang );
00057 if ( $blob ) {
00058 $blobs[$name] = $blob;
00059 }
00060 }
00061
00062 wfProfileOut( __METHOD__ );
00063 return $blobs;
00064 }
00065
00076 public static function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) {
00077 $blob = self::generateMessageBlob( $module, $lang );
00078
00079 if ( !$blob ) {
00080 return false;
00081 }
00082
00083 $dbw = wfGetDB( DB_MASTER );
00084 $success = $dbw->insert( 'msg_resource', array(
00085 'mr_lang' => $lang,
00086 'mr_resource' => $name,
00087 'mr_blob' => $blob,
00088 'mr_timestamp' => $dbw->timestamp()
00089 ),
00090 __METHOD__,
00091 array( 'IGNORE' )
00092 );
00093
00094 if ( $success ) {
00095 if ( $dbw->affectedRows() == 0 ) {
00096
00097 $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
00098 'mr_resource' => $name,
00099 'mr_lang' => $lang,
00100 ),
00101 __METHOD__
00102 );
00103 } else {
00104
00105 $rows = array();
00106
00107 foreach ( $module->getMessages() as $key ) {
00108 $rows[] = array(
00109 'mrl_resource' => $name,
00110 'mrl_message' => $key
00111 );
00112 }
00113 $dbw->insert( 'msg_resource_links', $rows,
00114 __METHOD__, array( 'IGNORE' )
00115 );
00116 }
00117 }
00118
00119 return $blob;
00120 }
00121
00130 public static function updateModule( $name, ResourceLoaderModule $module, $lang ) {
00131 $dbw = wfGetDB( DB_MASTER );
00132 $row = $dbw->selectRow( 'msg_resource', 'mr_blob',
00133 array( 'mr_resource' => $name, 'mr_lang' => $lang ),
00134 __METHOD__
00135 );
00136 if ( !$row ) {
00137 return null;
00138 }
00139
00140
00141 $oldBlob = $row->mr_blob;
00142 $newBlob = self::generateMessageBlob( $module, $lang );
00143
00144 $newRow = array(
00145 'mr_resource' => $name,
00146 'mr_lang' => $lang,
00147 'mr_blob' => $newBlob,
00148 'mr_timestamp' => $dbw->timestamp()
00149 );
00150
00151 $dbw->replace( 'msg_resource',
00152 array( array( 'mr_resource', 'mr_lang' ) ),
00153 $newRow, __METHOD__
00154 );
00155
00156
00157 $oldMessages = array_keys( FormatJson::decode( $oldBlob, true ) );
00158 $newMessages = array_keys( FormatJson::decode( $newBlob, true ) );
00159 $added = array_diff( $newMessages, $oldMessages );
00160 $removed = array_diff( $oldMessages, $newMessages );
00161
00162
00163 if ( $removed ) {
00164 $dbw->delete( 'msg_resource_links', array(
00165 'mrl_resource' => $name,
00166 'mrl_message' => $removed
00167 ), __METHOD__
00168 );
00169 }
00170
00171 $newLinksRows = array();
00172
00173 foreach ( $added as $message ) {
00174 $newLinksRows[] = array(
00175 'mrl_resource' => $name,
00176 'mrl_message' => $message
00177 );
00178 }
00179
00180 if ( $newLinksRows ) {
00181 $dbw->insert( 'msg_resource_links', $newLinksRows, __METHOD__,
00182 array( 'IGNORE' )
00183 );
00184 }
00185
00186 return $newBlob;
00187 }
00188
00194 public static function updateMessage( $key ) {
00195 $dbw = wfGetDB( DB_MASTER );
00196
00197
00198
00199
00200 $updates = null;
00201 do {
00202 $updates = self::getUpdatesForMessage( $key, $updates );
00203
00204 foreach ( $updates as $k => $update ) {
00205
00206
00207
00208 $success = $dbw->update( 'msg_resource',
00209 array(
00210 'mr_blob' => $update['newBlob'],
00211 'mr_timestamp' => $dbw->timestamp() ),
00212 array(
00213 'mr_resource' => $update['resource'],
00214 'mr_lang' => $update['lang'],
00215 'mr_timestamp' => $update['timestamp'] ),
00216 __METHOD__
00217 );
00218
00219
00220
00221
00222 if ( !( $success && $dbw->affectedRows() == 0 ) ) {
00223
00224 unset( $updates[$k] );
00225 }
00226 }
00227 } while ( count( $updates ) );
00228
00229
00230
00231 }
00232
00233 public static function clear() {
00234
00235
00236 $dbw = wfGetDB( DB_MASTER );
00237 $dbw->delete( 'msg_resource', '*', __METHOD__ );
00238 $dbw->delete( 'msg_resource_links', '*', __METHOD__ );
00239 }
00240
00248 private static function getUpdatesForMessage( $key, $prevUpdates = null ) {
00249 $dbw = wfGetDB( DB_MASTER );
00250
00251 if ( is_null( $prevUpdates ) ) {
00252
00253 $res = $dbw->select(
00254 array( 'msg_resource', 'msg_resource_links' ),
00255 array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
00256 array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ),
00257 __METHOD__
00258 );
00259 } else {
00260
00261
00262
00263
00264 $twoD = array();
00265
00266 foreach ( $prevUpdates as $update ) {
00267 $twoD[$update['resource']][$update['lang']] = true;
00268 }
00269
00270 $res = $dbw->select( 'msg_resource',
00271 array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ),
00272 $dbw->makeWhereFrom2d( $twoD, 'mr_resource', 'mr_lang' ),
00273 __METHOD__
00274 );
00275 }
00276
00277
00278 $updates = array();
00279
00280 foreach ( $res as $row ) {
00281 $updates[] = array(
00282 'resource' => $row->mr_resource,
00283 'lang' => $row->mr_lang,
00284 'timestamp' => $row->mr_timestamp,
00285 'newBlob' => self::reencodeBlob( $row->mr_blob, $key, $row->mr_lang )
00286 );
00287 }
00288
00289 return $updates;
00290 }
00291
00300 private static function reencodeBlob( $blob, $key, $lang ) {
00301 $decoded = FormatJson::decode( $blob, true );
00302 $decoded[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
00303
00304 return FormatJson::encode( (object)$decoded );
00305 }
00306
00317 private static function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) {
00318 global $wgCacheEpoch;
00319 $retval = array();
00320 $dbr = wfGetDB( DB_SLAVE );
00321 $res = $dbr->select( 'msg_resource',
00322 array( 'mr_blob', 'mr_resource', 'mr_timestamp' ),
00323 array( 'mr_resource' => $modules, 'mr_lang' => $lang ),
00324 __METHOD__
00325 );
00326
00327 foreach ( $res as $row ) {
00328 $module = $resourceLoader->getModule( $row->mr_resource );
00329 if ( !$module ) {
00330
00331 throw new MWException( __METHOD__ . ' passed an invalid module name' );
00332 }
00333
00334
00335 if ( array_keys( FormatJson::decode( $row->mr_blob, true ) ) !== array_values( array_unique( $module->getMessages() ) ) ||
00336 wfTimestamp( TS_MW, $row->mr_timestamp ) <= $wgCacheEpoch ) {
00337 $retval[$row->mr_resource] = self::updateModule( $row->mr_resource, $module, $lang );
00338 } else {
00339 $retval[$row->mr_resource] = $row->mr_blob;
00340 }
00341 }
00342
00343 return $retval;
00344 }
00345
00353 private static function generateMessageBlob( ResourceLoaderModule $module, $lang ) {
00354 $messages = array();
00355
00356 foreach ( $module->getMessages() as $key ) {
00357 $messages[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
00358 }
00359
00360 return FormatJson::encode( (object)$messages );
00361 }
00362 }