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
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
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
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
00214 $hugearray[$key][] = & $id;
00215 } else {
00216
00217
00218
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 );
00228 }
00229
00230
00231
00232
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
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
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
00514
00515
00516 $this->index = array();
00517 $this->index = $this->rebuild();
00518 }
00519
00520 return $this->reader = CdbReader::open( $file );
00521 }
00522 }