MessageBlobStore.php

Go to the documentation of this file.
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                 // Try getting from the DB first
00051                 $blobs = self::getFromDB( $resourceLoader, array_keys( $modules ), $lang );
00052 
00053                 // Generate blobs for any missing modules and store them in the DB
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                                 // Blob was already present, fetch it
00097                                 $blob = $dbw->selectField( 'msg_resource', 'mr_blob', array(
00098                                                 'mr_resource' => $name,
00099                                                 'mr_lang' => $lang,
00100                                         ),
00101                                         __METHOD__
00102                                 );
00103                         } else {
00104                                 // Update msg_resource_links
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                 // Save the old and new blobs for later
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                 // Figure out which messages were added and removed
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                 // Delete removed messages, insert added ones
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' ) // just in case
00183                         );
00184                 }
00185 
00186                 return $newBlob;
00187         }
00188 
00194         public static function updateMessage( $key ) {
00195                 $dbw = wfGetDB( DB_MASTER );
00196 
00197                 // Keep running until the updates queue is empty.
00198                 // Due to update conflicts, the queue might not be emptied
00199                 // in one iteration.
00200                 $updates = null;
00201                 do {
00202                         $updates = self::getUpdatesForMessage( $key, $updates );
00203 
00204                         foreach ( $updates as $k => $update ) {
00205                                 // Update the row on the condition that it
00206                                 // didn't change since we fetched it by putting
00207                                 // the timestamp in the WHERE clause.
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                                 // Only requeue conflicted updates.
00220                                 // If update() returned false, don't retry, for
00221                                 // fear of getting into an infinite loop
00222                                 if ( !( $success && $dbw->affectedRows() == 0 ) ) {
00223                                         // Not conflicted
00224                                         unset( $updates[$k] );
00225                                 }
00226                         }
00227                 } while ( count( $updates ) );
00228 
00229                 // No need to update msg_resource_links because we didn't add
00230                 // or remove any messages, we just changed their contents.
00231         }
00232 
00233         public static function clear() {
00234                 // TODO: Give this some more thought
00235                 // TODO: Is TRUNCATE better?
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                         // Fetch all blobs referencing $key
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                         // Refetch the blobs referenced by $prevUpdates
00261 
00262                         // Reorganize the (resource, lang) pairs in the format
00263                         // expected by makeWhereFrom2d()
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                 // Build the new updates queue
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                                 // This shouldn't be possible
00331                                 throw new MWException( __METHOD__ . ' passed an invalid module name' );
00332                         }
00333                         // Update the module's blobs if the set of messages changed or if the blob is
00334                         // older than $wgCacheEpoch
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 }
Generated on Fri Oct 19 00:00:59 2012 for MediaWiki by  doxygen 1.6.3