MediaWiki
REL1_19
|
00001 <?php 00031 class MessageBlobStore { 00032 00041 public static function get( ResourceLoader $resourceLoader, $modules, $lang ) { 00042 wfProfileIn( __METHOD__ ); 00043 if ( !count( $modules ) ) { 00044 wfProfileOut( __METHOD__ ); 00045 return array(); 00046 } 00047 // Try getting from the DB first 00048 $blobs = self::getFromDB( $resourceLoader, array_keys( $modules ), $lang ); 00049 00050 // Generate blobs for any missing modules and store them in the DB 00051 $missing = array_diff( array_keys( $modules ), array_keys( $blobs ) ); 00052 foreach ( $missing as $name ) { 00053 $blob = self::insertMessageBlob( $name, $modules[$name], $lang ); 00054 if ( $blob ) { 00055 $blobs[$name] = $blob; 00056 } 00057 } 00058 00059 wfProfileOut( __METHOD__ ); 00060 return $blobs; 00061 } 00062 00073 public static function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) { 00074 $blob = self::generateMessageBlob( $module, $lang ); 00075 00076 if ( !$blob ) { 00077 return false; 00078 } 00079 00080 $dbw = wfGetDB( DB_MASTER ); 00081 $success = $dbw->insert( 'msg_resource', array( 00082 'mr_lang' => $lang, 00083 'mr_resource' => $name, 00084 'mr_blob' => $blob, 00085 'mr_timestamp' => $dbw->timestamp() 00086 ), 00087 __METHOD__, 00088 array( 'IGNORE' ) 00089 ); 00090 00091 if ( $success ) { 00092 if ( $dbw->affectedRows() == 0 ) { 00093 // Blob was already present, fetch it 00094 $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array( 00095 'mr_resource' => $name, 00096 'mr_lang' => $lang, 00097 ), 00098 __METHOD__ 00099 ); 00100 } else { 00101 // Update msg_resource_links 00102 $rows = array(); 00103 00104 foreach ( $module->getMessages() as $key ) { 00105 $rows[] = array( 00106 'mrl_resource' => $name, 00107 'mrl_message' => $key 00108 ); 00109 } 00110 $dbw->insert( 'msg_resource_links', $rows, 00111 __METHOD__, array( 'IGNORE' ) 00112 ); 00113 } 00114 } 00115 00116 return $blob; 00117 } 00118 00127 public static function updateModule( $name, ResourceLoaderModule $module, $lang ) { 00128 $dbw = wfGetDB( DB_MASTER ); 00129 $row = $dbw->selectRow( 'msg_resource', 'mr_blob', 00130 array( 'mr_resource' => $name, 'mr_lang' => $lang ), 00131 __METHOD__ 00132 ); 00133 if ( !$row ) { 00134 return null; 00135 } 00136 00137 // Save the old and new blobs for later 00138 $oldBlob = $row->mr_blob; 00139 $newBlob = self::generateMessageBlob( $module, $lang ); 00140 00141 $newRow = array( 00142 'mr_resource' => $name, 00143 'mr_lang' => $lang, 00144 'mr_blob' => $newBlob, 00145 'mr_timestamp' => $dbw->timestamp() 00146 ); 00147 00148 $dbw->replace( 'msg_resource', 00149 array( array( 'mr_resource', 'mr_lang' ) ), 00150 $newRow, __METHOD__ 00151 ); 00152 00153 // Figure out which messages were added and removed 00154 $oldMessages = array_keys( FormatJson::decode( $oldBlob, true ) ); 00155 $newMessages = array_keys( FormatJson::decode( $newBlob, true ) ); 00156 $added = array_diff( $newMessages, $oldMessages ); 00157 $removed = array_diff( $oldMessages, $newMessages ); 00158 00159 // Delete removed messages, insert added ones 00160 if ( $removed ) { 00161 $dbw->delete( 'msg_resource_links', array( 00162 'mrl_resource' => $name, 00163 'mrl_message' => $removed 00164 ), __METHOD__ 00165 ); 00166 } 00167 00168 $newLinksRows = array(); 00169 00170 foreach ( $added as $message ) { 00171 $newLinksRows[] = array( 00172 'mrl_resource' => $name, 00173 'mrl_message' => $message 00174 ); 00175 } 00176 00177 if ( $newLinksRows ) { 00178 $dbw->insert( 'msg_resource_links', $newLinksRows, __METHOD__, 00179 array( 'IGNORE' ) // just in case 00180 ); 00181 } 00182 00183 return $newBlob; 00184 } 00185 00191 public static function updateMessage( $key ) { 00192 $dbw = wfGetDB( DB_MASTER ); 00193 00194 // Keep running until the updates queue is empty. 00195 // Due to update conflicts, the queue might not be emptied 00196 // in one iteration. 00197 $updates = null; 00198 do { 00199 $updates = self::getUpdatesForMessage( $key, $updates ); 00200 00201 foreach ( $updates as $k => $update ) { 00202 // Update the row on the condition that it 00203 // didn't change since we fetched it by putting 00204 // the timestamp in the WHERE clause. 00205 $success = $dbw->update( 'msg_resource', 00206 array( 00207 'mr_blob' => $update['newBlob'], 00208 'mr_timestamp' => $dbw->timestamp() ), 00209 array( 00210 'mr_resource' => $update['resource'], 00211 'mr_lang' => $update['lang'], 00212 'mr_timestamp' => $update['timestamp'] ), 00213 __METHOD__ 00214 ); 00215 00216 // Only requeue conflicted updates. 00217 // If update() returned false, don't retry, for 00218 // fear of getting into an infinite loop 00219 if ( !( $success && $dbw->affectedRows() == 0 ) ) { 00220 // Not conflicted 00221 unset( $updates[$k] ); 00222 } 00223 } 00224 } while ( count( $updates ) ); 00225 00226 // No need to update msg_resource_links because we didn't add 00227 // or remove any messages, we just changed their contents. 00228 } 00229 00230 public static function clear() { 00231 // TODO: Give this some more thought 00232 // TODO: Is TRUNCATE better? 00233 $dbw = wfGetDB( DB_MASTER ); 00234 $dbw->delete( 'msg_resource', '*', __METHOD__ ); 00235 $dbw->delete( 'msg_resource_links', '*', __METHOD__ ); 00236 } 00237 00245 private static function getUpdatesForMessage( $key, $prevUpdates = null ) { 00246 $dbw = wfGetDB( DB_MASTER ); 00247 00248 if ( is_null( $prevUpdates ) ) { 00249 // Fetch all blobs referencing $key 00250 $res = $dbw->select( 00251 array( 'msg_resource', 'msg_resource_links' ), 00252 array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ), 00253 array( 'mrl_message' => $key, 'mr_resource=mrl_resource' ), 00254 __METHOD__ 00255 ); 00256 } else { 00257 // Refetch the blobs referenced by $prevUpdates 00258 00259 // Reorganize the (resource, lang) pairs in the format 00260 // expected by makeWhereFrom2d() 00261 $twoD = array(); 00262 00263 foreach ( $prevUpdates as $update ) { 00264 $twoD[$update['resource']][$update['lang']] = true; 00265 } 00266 00267 $res = $dbw->select( 'msg_resource', 00268 array( 'mr_resource', 'mr_lang', 'mr_blob', 'mr_timestamp' ), 00269 $dbw->makeWhereFrom2d( $twoD, 'mr_resource', 'mr_lang' ), 00270 __METHOD__ 00271 ); 00272 } 00273 00274 // Build the new updates queue 00275 $updates = array(); 00276 00277 foreach ( $res as $row ) { 00278 $updates[] = array( 00279 'resource' => $row->mr_resource, 00280 'lang' => $row->mr_lang, 00281 'timestamp' => $row->mr_timestamp, 00282 'newBlob' => self::reencodeBlob( $row->mr_blob, $key, $row->mr_lang ) 00283 ); 00284 } 00285 00286 return $updates; 00287 } 00288 00297 private static function reencodeBlob( $blob, $key, $lang ) { 00298 $decoded = FormatJson::decode( $blob, true ); 00299 $decoded[$key] = wfMsgExt( $key, array( 'language' => $lang ) ); 00300 00301 return FormatJson::encode( (object)$decoded ); 00302 } 00303 00313 private static function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) { 00314 global $wgCacheEpoch; 00315 $retval = array(); 00316 $dbr = wfGetDB( DB_SLAVE ); 00317 $res = $dbr->select( 'msg_resource', 00318 array( 'mr_blob', 'mr_resource', 'mr_timestamp' ), 00319 array( 'mr_resource' => $modules, 'mr_lang' => $lang ), 00320 __METHOD__ 00321 ); 00322 00323 foreach ( $res as $row ) { 00324 $module = $resourceLoader->getModule( $row->mr_resource ); 00325 if ( !$module ) { 00326 // This shouldn't be possible 00327 throw new MWException( __METHOD__ . ' passed an invalid module name' ); 00328 } 00329 // Update the module's blobs if the set of messages changed or if the blob is 00330 // older than $wgCacheEpoch 00331 if ( array_keys( FormatJson::decode( $row->mr_blob, true ) ) !== array_values( array_unique( $module->getMessages() ) ) || 00332 wfTimestamp( TS_MW, $row->mr_timestamp ) <= $wgCacheEpoch ) { 00333 $retval[$row->mr_resource] = self::updateModule( $row->mr_resource, $module, $lang ); 00334 } else { 00335 $retval[$row->mr_resource] = $row->mr_blob; 00336 } 00337 } 00338 00339 return $retval; 00340 } 00341 00349 private static function generateMessageBlob( ResourceLoaderModule $module, $lang ) { 00350 $messages = array(); 00351 00352 foreach ( $module->getMessages() as $key ) { 00353 $messages[$key] = wfMsgExt( $key, array( 'language' => $lang ) ); 00354 } 00355 00356 return FormatJson::encode( (object)$messages ); 00357 } 00358 }