Merge "doc: various updates"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 30 May 2013 08:46:35 +0000 (08:46 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 30 May 2013 08:46:35 +0000 (08:46 +0000)
1  2 
includes/cache/MessageCache.php

   */
  
  /**
-  *
+  * MediaWiki message cache structure version.
+  * Bump this whenever the message cache format has changed.
+  */
+ define( 'MSG_CACHE_VERSION', 1 );
+ /**
+  * Memcached timeout when loading a key.
+  * See MessageCache::load()
   */
  define( 'MSG_LOAD_TIMEOUT', 60 );
+ /**
+  * Memcached timeout when locking a key for a writing operation.
+  * See MessageCache::lock()
+  */
  define( 'MSG_LOCK_TIMEOUT', 30 );
+ /**
+  * Number of times we will try to acquire a lock from Memcached.
+  * This comes in addition to MSG_LOCK_TIMEOUT.
+  */
  define( 'MSG_WAIT_TIMEOUT', 30 );
- define( 'MSG_CACHE_VERSION', 1 );
  
  /**
   * Message cache
@@@ -44,10 -59,16 +59,16 @@@ class MessageCache 
         */
        protected $mCache;
  
-       // Should  mean that database cannot be used, but check
+       /**
+        * Should  mean that database cannot be used, but check
+        * @var bool $mDisable
+        */
        protected $mDisable;
  
-       /// Lifetime for cache, used by object caching
+       /**
+        * Lifetime for cache, used by object caching.
+        * Set on construction, see __construct().
+        */
        protected $mExpiry;
  
        /**
         */
        protected $mParserOptions, $mParser;
  
-       /// Variable for tracking which variables are already loaded
+       /**
+        * Variable for tracking which variables are already loaded
+        * @var array $mLoadedLanguages
+        */
        protected $mLoadedLanguages = array();
  
        /**
         * Singleton instance
         *
-        * @var MessageCache
+        * @var MessageCache $instance
         */
        private static $instance;
  
        /**
-        * @var bool
+        * @var bool $mInParser
         */
        protected $mInParser = false;
  
                self::$instance = null;
        }
  
+       /**
+        * @param ObjectCache $memCached A cache instance. If none, fall back to CACHE_NONE.
+        * @param bool $useDB
+        * @param int $expiry Lifetime for cache. @see $mExpiry.
+        */
        function __construct( $memCached, $useDB, $expiry ) {
                if ( !$memCached ) {
                        $memCached = wfGetCache( CACHE_NONE );
        }
  
        /**
-        * Represents a write lock on the messages key
+        * Represents a write lock on the messages key.
+        *
+        * Will retry MessageCache::MSG_WAIT_TIMEOUT times, each operations having
+        * a timeout of MessageCache::MSG_LOCK_TIMEOUT.
         *
         * @param string $key
         * @return Boolean: success
        /**
         * Get a message from either the content language or the user language.
         *
 -       * @param string $key The message cache key
 -       * @param bool $useDB Get the message from the DB, false to use only
 -       *               the localisation
 -       * @param bool|string $langcode Code of the language to get the message for, if
 -       *                  it is a valid code create a language for that language,
 -       *                  if it is a string but not a valid code then make a basic
 -       *                  language object, if it is a false boolean then use the
 -       *                  current users language (as a fallback for the old
 -       *                  parameter functionality), or if it is a true boolean
 -       *                  then use the wikis content language (also as a
 -       *                  fallback).
 -       * @param bool $isFullKey Specifies whether $key is a two part key "msg/lang".
 +       * First, assemble a list of languages to attempt getting the message from. This
 +       * chain begins with the requested language and its fallbacks and then continues with
 +       * the content language and its fallbacks. For each language in the chain, the following
 +       * process will occur (in this order):
 +       *  1. If a language-specific override, i.e., [[MW:msg/lang]], is available, use that.
 +       *     Note: for the content language, there is no /lang subpage.
 +       *  2. Fetch from the static CDB cache.
 +       *  3. If available, check the database for fallback language overrides.
         *
 -       * @throws MWException
 -       * @return string|bool
 +       * This process provides a number of guarantees. When changing this code, make sure all
 +       * of these guarantees are preserved.
 +       *  * If the requested language is *not* the content language, then the CDB cache for that
 +       *    specific language will take precedence over the root database page ([[MW:msg]]).
 +       *  * Fallbacks will be just that: fallbacks. A fallback language will never be reached if
 +       *    the message is available *anywhere* in the language for which it is a fallback.
 +       *
 +       * @param string $key the message key
 +       * @param bool $useDB If true, look for the message in the DB, false
 +       *                    to use only the compiled l10n cache.
 +       * @param bool|string|object $langcode Code of the language to get the message for.
 +       *        - If string and a valid code, will create a standard language object
 +       *        - If string but not a valid code, will create a basic language object
 +       *        - If boolean and false, create object from the current users language
 +       *        - If boolean and true, create object from the wikis content language
 +       *        - If language object, use it as given
 +       * @param bool $isFullKey specifies whether $key is a two part key
 +       *                   "msg/lang".
 +       *
 +       * @throws MWException when given an invalid key
 +       * @return string|bool False if the message doesn't exist, otherwise the message (which can be empty)
         */
        function get( $key, $useDB = true, $langcode = true, $isFullKey = false ) {
 -              global $wgLanguageCode, $wgContLang;
 +              global $wgContLang;
  
 -              if ( is_int( $key ) ) {
 -                      // "Non-string key given" exception sometimes happens for numerical
 -                      // strings that become ints somewhere on their way here
 -                      $key = strval( $key );
 -              }
 +              $section = new ProfileSection( __METHOD__ );
  
 -              if ( !is_string( $key ) ) {
 +              if ( is_int( $key ) ) {
 +                      // Fix numerical strings that somehow become ints
 +                      // on their way here
 +                      $key = (string)$key;
 +              } elseif ( !is_string( $key ) ) {
                        throw new MWException( 'Non-string key given' );
 -              }
 -
 -              if ( strval( $key ) === '' ) {
 -                      # Shortcut: the empty key is always missing
 +              } elseif ( $key === '' ) {
 +                      // Shortcut: the empty key is always missing
                        return false;
                }
  
 -              $lang = wfGetLangObj( $langcode );
 -              if ( !$lang ) {
 -                      throw new MWException( "Bad lang code $langcode given" );
 +              // For full keys, get the language code from the key
 +              $pos = strrpos( $key, '/' );
 +              if ( $isFullKey && $pos !== false ) {
 +                      $langcode = substr( $key, $pos + 1 );
 +                      $key = substr( $key, 0, $pos );
                }
  
 -              $langcode = $lang->getCode();
 -
 -              $message = false;
 -
 -              # Normalise title-case input (with some inlining)
 -              $lckey = str_replace( ' ', '_', $key );
 +              // Normalise title-case input (with some inlining)
 +              $lckey = strtr( $key, ' ', '_');
                if ( ord( $key ) < 128 ) {
                        $lckey[0] = strtolower( $lckey[0] );
                        $uckey = ucfirst( $lckey );
                        $uckey = $wgContLang->ucfirst( $lckey );
                }
  
 -              # Try the MediaWiki namespace
 -              if ( !$this->mDisable && $useDB ) {
 -                      $title = $uckey;
 -                      if ( !$isFullKey && ( $langcode != $wgLanguageCode ) ) {
 -                              $title .= '/' . $langcode;
 -                      }
 -                      $message = $this->getMsgFromNamespace( $title, $langcode );
 -              }
 -
 -              # Try the array in the language object
 -              if ( $message === false ) {
 -                      $message = $lang->getMessage( $lckey );
 -                      if ( is_null( $message ) ) {
 -                              $message = false;
 -                      }
 -              }
 +              // Loop through each language in the fallback list until we find something useful
 +              $lang = wfGetLangObj( $langcode );
 +              $message = $this->getMessageFromFallbackChain( $lang, $lckey, $uckey, !$this->mDisable && $useDB );
  
 -              # Try the array of another language
 +              // If we still have no message, maybe the key was in fact a full key so try that
                if ( $message === false ) {
                        $parts = explode( '/', $lckey );
 -                      # We may get calls for things that are http-urls from sidebar
 -                      # Let's not load nonexistent languages for those
 -                      # They usually have more than one slash.
 +                      // We may get calls for things that are http-urls from sidebar
 +                      // Let's not load nonexistent languages for those
 +                      // They usually have more than one slash.
                        if ( count( $parts ) == 2 && $parts[1] !== '' ) {
                                $message = Language::getMessageFor( $parts[0], $parts[1] );
 -                              if ( is_null( $message ) ) {
 +                              if ( $message === null ) {
                                        $message = false;
                                }
                        }
                }
  
 -              # Is this a custom message? Try the default language in the db...
 -              if ( ( $message === false || $message === '-' ) &&
 -                      !$this->mDisable && $useDB &&
 -                      !$isFullKey && ( $langcode != $wgLanguageCode )
 -              ) {
 +              // Post-processing if the message exists
 +              if( $message !== false ) {
 +                      // Fix whitespace
 +                      $message = str_replace(
 +                              array(
 +                                      # Fix for trailing whitespace, removed by textarea
 +                                      '&#32;',
 +                                      # Fix for NBSP, converted to space by firefox
 +                                      '&nbsp;',
 +                                      '&#160;',
 +                              ),
 +                              array(
 +                                      ' ',
 +                                      "\xc2\xa0",
 +                                      "\xc2\xa0"
 +                              ),
 +                              $message
 +                      );
 +              }
 +
 +              return $message;
 +      }
 +
 +      /**
 +       * Given a language, try and fetch a message from that language, then the
 +       * fallbacks of that language, then the site language, then the fallbacks for the
 +       * site language.
 +       *
 +       * @param Language $lang Requested language
 +       * @param string $lckey Lowercase key for the message
 +       * @param string $uckey Uppercase key for the message
 +       * @param bool $useDB Whether to use the database
 +       *
 +       * @see MessageCache::get
 +       * @return string|bool The message, or false if not found
 +       */
 +      protected function getMessageFromFallbackChain( $lang, $lckey, $uckey, $useDB ) {
 +              global $wgLanguageCode, $wgContLang;
 +
 +              $langcode = $lang->getCode();
 +              $message = false;
 +
 +              // First try the requested language.
 +              if ( $useDB ) {
 +                      if ( $langcode === $wgLanguageCode ) {
 +                              // Messages created in the content language will not have the /lang extension
 +                              $message = $this->getMsgFromNamespace( $uckey, $langcode );
 +                      } else {
 +                              $message = $this->getMsgFromNamespace( "$uckey/$langcode", $langcode );
 +                      }
 +              }
 +
 +              if ( $message !== false ) {
 +                      return $message;
 +              }
 +
 +              // Check the CDB cache
 +              $message = $lang->getMessage( $lckey );
 +              if ( $message !== null ) {
 +                      return $message;
 +              }
 +
 +              list( $fallbackChain, $siteFallbackChain ) = Language::getFallbacksIncludingSiteLanguage( $langcode );
 +
 +              // Next try checking the database for all of the fallback languages of the requested language.
 +              if ( $useDB ) {
 +                      foreach ( $fallbackChain as $code ) {
 +                              if ( $code === $wgLanguageCode ) {
 +                                      // Messages created in the content language will not have the /lang extension
 +                                      $message = $this->getMsgFromNamespace( $uckey, $code );
 +                              } else {
 +                                      $message = $this->getMsgFromNamespace( "$uckey/$code", $code );
 +                              }
 +
 +                              if ( $message !== false ) {
 +                                      // Found the message.
 +                                      return $message;
 +                              }
 +                      }
 +              }
 +
 +              // Now try checking the site language.
 +              if ( $useDB ) {
                        $message = $this->getMsgFromNamespace( $uckey, $wgLanguageCode );
 +                      if ( $message !== false ) {
 +                              return $message;
 +                      }
                }
  
 -              # Final fallback
 -              if ( $message === false ) {
 -                      return false;
 +              $message = $wgContLang->getMessage( $lckey );
 +              if ( $message !== null ) {
 +                      return $message;
                }
  
 -              # Fix whitespace
 -              $message = strtr( $message,
 -                      array(
 -                              # Fix for trailing whitespace, removed by textarea
 -                              '&#32;' => ' ',
 -                              # Fix for NBSP, converted to space by firefox
 -                              '&nbsp;' => "\xc2\xa0",
 -                              '&#160;' => "\xc2\xa0",
 -                      ) );
 +              // Finally try the DB for the site language's fallbacks.
 +              if ( $useDB ) {
 +                      foreach ( $siteFallbackChain as $code ) {
 +                              $message = $this->getMsgFromNamespace( "$uckey/$code", $code );
 +                              if ( $message === false && $code === $wgLanguageCode ) {
 +                                      // Messages created in the content language will not have the /lang extension
 +                                      $message = $this->getMsgFromNamespace( $uckey, $code );
 +                              }
  
 -              return $message;
 +                              if ( $message !== false ) {
 +                                      // Found the message.
 +                                      return $message;
 +                              }
 +                      }
 +              }
 +
 +              return false;
        }
  
        /**