Index: languages/Language.php =================================================================== --- languages/Language.php (revision 93959) +++ languages/Language.php (working copy) @@ -148,9 +148,6 @@ * @return Language */ protected static function newFromCode( $code ) { - global $IP; - static $recursionLevel = 0; - // Protect against path traversal below if ( !Language::isValidCode( $code ) || strcspn( $code, ":/\\\000" ) !== strlen( $code ) ) @@ -166,35 +163,31 @@ return $lang; } - if ( $code == 'en' ) { - $class = 'Language'; - } else { - $class = 'Language' . str_replace( '-', '_', ucfirst( $code ) ); - if ( !defined( 'MW_COMPILED' ) ) { - // Preload base classes to work around APC/PHP5 bug - if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) { - include_once( "$IP/languages/classes/$class.deps.php" ); - } - if ( file_exists( "$IP/languages/classes/$class.php" ) ) { - include_once( "$IP/languages/classes/$class.php" ); - } - } + // Check if there is a language class for the code + $class = self::classFromCode( $code ); + self::preloadLanguageClass( $class ); + if ( MWInit::classExists( $class ) ) { + $lang = Language::newFromCode( $code ); + return $lang; } - if ( $recursionLevel > 5 ) { - throw new MWException( "Language fallback loop detected when creating class $class\n" ); + // Keep trying the fallback list until we find an existing class + $fallbacks = Language::getFallbacskFor( $code ); + foreach ( $fallbacks as $fallbackCode ) { + if ( !Language::isValidBuiltInCode( $fallbackCode ) ) { + throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" ); + } + + $class = self::classFromCode( $fallbackCode ); + self::preloadLanguageClass( $class ); + if ( MWInit::classExists( $class ) ) { + $lang = Language::newFromCode( $fallbackCode ); + $lang->setCode( $code ); + return $lang; + } } - if ( !MWInit::classExists( $class ) ) { - $fallback = Language::getFallbackFor( $code ); - ++$recursionLevel; - $lang = Language::newFromCode( $fallback ); - --$recursionLevel; - $lang->setCode( $code ); - } else { - $lang = new $class; - } - return $lang; + throw new MWException( "Invalid fallback sequence for language '$code'" ); } /** @@ -224,6 +217,32 @@ return preg_match( '/^[a-z0-9-]*$/i', $code ); } + public static function classFromCode( $code ) { + if ( $code == 'en' ) { + return 'Language'; + } else { + return 'Language' . str_replace( '-', '_', ucfirst( $code ) ); + } + } + + public static function preloadLanguageClass( $class ) { + global $IP; + + if ( $class === 'Language' ) { + return; + } + + if ( !defined( 'MW_COMPILED' ) ) { + // Preload base classes to work around APC/PHP5 bug + if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) { + include_once( "$IP/languages/classes/$class.deps.php" ); + } + if ( file_exists( "$IP/languages/classes/$class.php" ) ) { + include_once( "$IP/languages/classes/$class.php" ); + } + } + } + /** * Get the LocalisationCache instance * @@ -2437,14 +2451,7 @@ */ function addMagicWordsByLang( $newWords ) { $code = $this->getCode(); - $fallbackChain = array(); - while ( $code && !in_array( $code, $fallbackChain ) ) { - $fallbackChain[] = $code; - $code = self::getFallbackFor( $code ); - } - if ( !in_array( 'en', $fallbackChain ) ) { - $fallbackChain[] = 'en'; - } + $fallbackChain = self::getFallbacskFor( $code ); $fallbackChain = array_reverse( $fallbackChain ); foreach ( $fallbackChain as $code ) { if ( isset( $newWords[$code] ) ) { @@ -3304,11 +3311,29 @@ // Shortcut return false; } else { - return self::getLocalisationCache()->getItem( $code, 'fallback' ); + $fallbacks = self::getFallbacskFor( $code ); + // Thank you, php + $first = array_shift( $fallbacks ); + return $first; } } /** + * Get the ordered list of fallback languages. + * + * @param $code string Language code + * @return array + */ + static function getFallbacskFor( $code ) { + if ( $code === 'en' ) { + // Shortcut + return array(); + } else { + return (array) self::getLocalisationCache()->getItem( $code, 'fallback' ); + } + } + + /** * Get all messages for a given language * WARNING: this may take a long time *