MessageIndex.php

Go to the documentation of this file.
00001 <?php
00019 abstract class MessageIndex {
00021     protected static $instance;
00022 
00026     public static function singleton() {
00027         if ( self::$instance === null ) {
00028             global $wgTranslateMessageIndex;
00029             $params = $wgTranslateMessageIndex;
00030             $class = array_shift( $params );
00031             self::$instance = new $class( $params );
00032         }
00033 
00034         return self::$instance;
00035     }
00036 
00043     public static function getGroupIds( MessageHandle $handle ) {
00044         $namespace = $handle->getTitle()->getNamespace();
00045         $key = $handle->getKey();
00046         $normkey = TranslateUtils::normaliseKey( $namespace, $key );
00047 
00048         $value = self::singleton()->get( $normkey );
00049         if ( $value !== null ) {
00050             return (array)$value;
00051         } else {
00052             return array();
00053         }
00054     }
00055 
00061     public static function getPrimaryGroupId( MessageHandle $handle ) {
00062         $groups = self::getGroupIds( $handle );
00063 
00064         return count( $groups ) ? array_shift( $groups ) : null;
00065     }
00066 
00073     protected function get( $key ) {
00074         // Default implementation
00075         $mi = $this->retrieve();
00076         if ( isset( $mi[$key] ) ) {
00077             return $mi[$key];
00078         } else {
00079             return null;
00080         }
00081     }
00082 
00084     abstract public function retrieve();
00085 
00086     abstract protected function store( array $array );
00087 
00088     public function rebuild() {
00089         static $recursion = 0;
00090 
00091         if ( $recursion > 0 ) {
00092             $msg = __METHOD__ . ': trying to recurse - building the index first time?';
00093             wfWarn( $msg );
00094 
00095             return array();
00096         }
00097         $recursion++;
00098 
00099         $groups = MessageGroups::singleton()->getGroups();
00100 
00101         $new = $old = array();
00102         $old = $this->retrieve();
00103         $postponed = array();
00104 
00108         foreach ( $groups as $g ) {
00109             if ( !$g->exists() ) {
00110                 continue;
00111             }
00112 
00113             # Skip meta thingies
00114             if ( $g->isMeta() ) {
00115                 $postponed[] = $g;
00116                 continue;
00117             }
00118 
00119             $this->checkAndAdd( $new, $g );
00120         }
00121 
00122         foreach ( $postponed as $g ) {
00123             $this->checkAndAdd( $new, $g, true );
00124         }
00125 
00126         $this->store( $new );
00127         $this->clearMessageGroupStats( $old, $new );
00128         $recursion--;
00129 
00130         return $new;
00131     }
00132 
00138     protected function clearMessageGroupStats( array $old, array $new ) {
00139         $changes = array();
00140 
00141         foreach ( $new as $key => $groups ) {
00142             // Using != here on purpose to ignore order of items
00143             if ( !isset( $old[$key] ) ) {
00144                 $changes[$key] = array( array(), (array)$groups );
00145             } elseif ( $groups != $old[$key] ) {
00146                 $changes[$key] = array( (array)$old[$key], (array)$groups );
00147             }
00148         }
00149 
00150         foreach ( $old as $key => $groups ) {
00151             if ( !isset( $new[$key] ) ) {
00152                 $changes[$key] = array( (array)$groups, array() );
00153             }
00154             // We already checked for diffs above
00155         }
00156 
00157         $changedGroups = array();
00158         foreach ( $changes as $data ) {
00159             foreach ( $data[0] as $group ) {
00160                 $changedGroups[$group] = true;
00161             }
00162             foreach ( $data[1] as $group ) {
00163                 $changedGroups[$group] = true;
00164             }
00165         }
00166 
00167         MessageGroupStats::clearGroup( array_keys( $changedGroups ) );
00168 
00169         foreach ( $changes as $key => $data ) {
00170             list( $ns, $pagename ) = explode( ':', $key, 2 );
00171             $title = Title::makeTitle( $ns, $pagename );
00172             $handle = new MessageHandle( $title );
00173             list ( $oldGroups, $newGroups ) = $data;
00174             wfRunHooks( 'TranslateEventMessageMembershipChange',
00175                 array( $handle, $oldGroups, $newGroups ) );
00176         }
00177     }
00178 
00184     protected function checkAndAdd( &$hugearray, MessageGroup $g, $ignore = false ) {
00185         if ( method_exists( $g, 'getKeys' ) ) {
00186             $keys = $g->getKeys();
00187         } else {
00188             $messages = $g->getDefinitions();
00189 
00190             if ( !is_array( $messages ) ) {
00191                 return;
00192             }
00193 
00194             $keys = array_keys( $messages );
00195         }
00196 
00197         $id = $g->getId();
00198 
00199         $namespace = $g->getNamespace();
00200 
00201         foreach ( $keys as $key ) {
00202             # Force all keys to lower case, because the case doesn't matter and it is
00203             # easier to do comparing when the case of first letter is unknown, because
00204             # mediawiki forces it to upper case
00205             $key = TranslateUtils::normaliseKey( $namespace, $key );
00206             if ( isset( $hugearray[$key] ) ) {
00207                 if ( !$ignore ) {
00208                     $to = implode( ', ', (array)$hugearray[$key] );
00209                     wfWarn( "Key $key already belongs to $to, conflict with $id" );
00210                 }
00211 
00212                 if ( is_array( $hugearray[$key] ) ) {
00213                     // Hard work is already done, just add a new reference
00214                     $hugearray[$key][] = & $id;
00215                 } else {
00216                     // Store the actual reference, then remove it from array, to not
00217                     // replace the references value, but to store an array of new
00218                     // references instead. References are hard!
00219                     $value = & $hugearray[$key];
00220                     unset( $hugearray[$key] );
00221                     $hugearray[$key] = array( &$value, &$id );
00222                 }
00223             } else {
00224                 $hugearray[$key] = & $id;
00225             }
00226         }
00227         unset( $id ); // Disconnect the previous references to this $id
00228     }
00229 
00230     /* These are probably slower than serialize and unserialize,
00231      * but they are more space efficient because we only need
00232      * strings and arrays. */
00233     protected function serialize( $data ) {
00234         if ( is_array( $data ) ) {
00235             return implode( '|', $data );
00236         } else {
00237             return $data;
00238         }
00239     }
00240 
00241     protected function unserialize( $data ) {
00242         if ( strpos( $data, '|' ) !== false ) {
00243             return explode( '|', $data );
00244         }
00245 
00246         return $data;
00247     }
00248 }
00249 
00264 class SerializedMessageIndex extends MessageIndex {
00266     protected $index;
00267 
00268     protected $filename = 'translate_messageindex.ser';
00269 
00271     public function retrieve() {
00272         if ( $this->index !== null ) {
00273             return $this->index;
00274         }
00275 
00276         wfProfileIn( __METHOD__ );
00277         $file = TranslateUtils::cacheFile( $this->filename );
00278         if ( file_exists( $file ) ) {
00279             $this->index = unserialize( file_get_contents( $file ) );
00280         } else {
00281             $this->index = $this->rebuild();
00282         }
00283         wfProfileOut( __METHOD__ );
00284 
00285         return $this->index;
00286     }
00287 
00288     protected function store( array $array ) {
00289         wfProfileIn( __METHOD__ );
00290         $file = TranslateUtils::cacheFile( $this->filename );
00291         file_put_contents( $file, serialize( $array ) );
00292         $this->index = $array;
00293         wfProfileOut( __METHOD__ );
00294     }
00295 }
00296 
00298 class FileCachedMessageIndex extends SerializedMessageIndex {
00299 }
00300 
00312 class DatabaseMessageIndex extends MessageIndex {
00314     protected $index;
00315 
00317     public function retrieve() {
00318         if ( $this->index !== null ) {
00319             return $this->index;
00320         }
00321 
00322         wfProfileIn( __METHOD__ );
00323         $dbr = wfGetDB( DB_SLAVE );
00324         $res = $dbr->select( 'translate_messageindex', '*', array(), __METHOD__ );
00325         $this->index = array();
00326         foreach ( $res as $row ) {
00327             $this->index[$row->tmi_key] = $this->unserialize( $row->tmi_value );
00328         }
00329         wfProfileOut( __METHOD__ );
00330 
00331         return $this->index;
00332     }
00333 
00334     protected function get( $key ) {
00335         wfProfileIn( __METHOD__ );
00336         $dbr = wfGetDB( DB_SLAVE );
00337         $value = $dbr->selectField(
00338             'translate_messageindex',
00339             'tmi_value',
00340             array( 'tmi_key' => $key ),
00341             __METHOD__
00342         );
00343 
00344         if ( is_string( $value ) ) {
00345             $value = $this->unserialize( $value );
00346         } else {
00347             $value = null;
00348         }
00349 
00350         wfProfileOut( __METHOD__ );
00351 
00352         return $value;
00353     }
00354 
00355     protected function store( array $array ) {
00356         wfProfileIn( __METHOD__ );
00357         $dbw = wfGetDB( DB_MASTER );
00358         $rows = array();
00359 
00360         foreach ( $array as $key => $value ) {
00361             $value = $this->serialize( $value );
00362             $rows[] = array( 'tmi_key' => $key, 'tmi_value' => $value );
00363         }
00364 
00365         $dbw->delete( 'translate_messageindex', '*', __METHOD__ );
00366         $dbw->replace( 'translate_messageindex', array( array( 'tmi_key' ) ), $rows, __METHOD__ );
00367 
00368         $this->index = $array;
00369         wfProfileOut( __METHOD__ );
00370     }
00371 }
00372 
00381 class CachedMessageIndex extends MessageIndex {
00382     protected $key = 'translate-messageindex';
00383     protected $cache;
00384 
00386     protected $index;
00387 
00388     protected function __construct( array $params ) {
00389         $this->cache = wfGetCache( CACHE_ANYTHING );
00390     }
00391 
00393     public function retrieve() {
00394         if ( $this->index !== null ) {
00395             return $this->index;
00396         }
00397 
00398         wfProfileIn( __METHOD__ );
00399         $key = wfMemckey( $this->key );
00400         $data = $this->cache->get( $key );
00401         if ( is_array( $data ) ) {
00402             $this->index = $data;
00403         } else {
00404             $this->index = $this->rebuild();
00405         }
00406         wfProfileOut( __METHOD__ );
00407 
00408         return $this->index;
00409     }
00410 
00411     protected function store( array $array ) {
00412         wfProfileIn( __METHOD__ );
00413         $key = wfMemckey( $this->key );
00414         $this->cache->set( $key, $array );
00415 
00416         $this->index = $array;
00417         wfProfileOut( __METHOD__ );
00418     }
00419 }
00420 
00434 class CDBMessageIndex extends MessageIndex {
00436     protected $index;
00437 
00439     protected $reader;
00440 
00442     protected $filename = 'translate_messageindex.cdb';
00443 
00445     public function retrieve() {
00446         $reader = $this->getReader();
00447         // This must be below the line above, which may fill the index
00448         if ( $this->index !== null ) {
00449             return $this->index;
00450         }
00451 
00452         wfProfileIn( __METHOD__ );
00453         $keys = (array)$this->unserialize( $reader->get( '#keys' ) );
00454         $this->index = array();
00455         foreach ( $keys as $key ) {
00456             $this->index[$key] = $this->unserialize( $reader->get( $key ) );
00457         }
00458         wfProfileOut( __METHOD__ );
00459 
00460         return $this->index;
00461     }
00462 
00463     protected function get( $key ) {
00464         $reader = $this->getReader();
00465         // We might have the full cache loaded
00466         if ( $this->index !== null ) {
00467             if ( isset( $this->index[$key] ) ) {
00468                 return $this->index[$key];
00469             } else {
00470                 return null;
00471             }
00472         }
00473 
00474         wfProfileIn( __METHOD__ );
00475         $value = $reader->get( $key );
00476         if ( !is_string( $value ) ) {
00477             $value = null;
00478         } else {
00479             $value = $this->unserialize( $value );
00480         }
00481         wfProfileOut( __METHOD__ );
00482 
00483         return $value;
00484     }
00485 
00486     protected function store( array $array ) {
00487         wfProfileIn( __METHOD__ );
00488         $this->reader = null;
00489 
00490         $file = TranslateUtils::cacheFile( $this->filename );
00491         $cache = CdbWriter::open( $file );
00492         $keys = array_keys( $array );
00493         $cache->set( '#keys', $this->serialize( $keys ) );
00494 
00495         foreach ( $array as $key => $value ) {
00496             $value = $this->serialize( $value );
00497             $cache->set( $key, $value );
00498         }
00499 
00500         $cache->close();
00501 
00502         $this->index = $array;
00503         wfProfileOut( __METHOD__ );
00504     }
00505 
00506     protected function getReader() {
00507         if ( $this->reader ) {
00508             return $this->reader;
00509         }
00510 
00511         $file = TranslateUtils::cacheFile( $this->filename );
00512         if ( !file_exists( $file ) ) {
00513             /* The rebuild() will call retrieve(), which we prevent from
00514              * recursing by setting the index to empty array now.
00515              */
00516             $this->index = array();
00517             $this->index = $this->rebuild();
00518         }
00519 
00520         return $this->reader = CdbReader::open( $file );
00521     }
00522 }
Generated on Tue Oct 29 00:00:26 2013 for MediaWiki Translate Extension by  doxygen 1.6.3