Attempt to fix Bug 30216 - Improve language fallback loop detection.
authorNiklas Laxström <nikerabbit@users.mediawiki.org>
Thu, 18 Aug 2011 16:41:07 +0000 (16:41 +0000)
committerNiklas Laxström <nikerabbit@users.mediawiki.org>
Thu, 18 Aug 2011 16:41:07 +0000 (16:41 +0000)
Made fallbacks a list per language, instead of scanning them recursively through the languages.

20 files changed:
includes/api/ApiQuerySiteinfo.php
languages/Language.php
languages/messages/MessagesAls.php
languages/messages/MessagesBat_smg.php
languages/messages/MessagesFiu_vro.php
languages/messages/MessagesGan.php
languages/messages/MessagesGan_hant.php
languages/messages/MessagesIi.php
languages/messages/MessagesKaa.php
languages/messages/MessagesKk_cn.php
languages/messages/MessagesKk_tr.php
languages/messages/MessagesMap_bms.php
languages/messages/MessagesQug.php
languages/messages/MessagesRue.php
languages/messages/MessagesRuq.php
languages/messages/MessagesTt.php
languages/messages/MessagesZh_hk.php
languages/messages/MessagesZh_mo.php
languages/messages/MessagesZh_my.php
languages/messages/MessagesZh_tw.php

index 6859604..3c61824 100644 (file)
@@ -140,13 +140,11 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data['rights'] = $GLOBALS['wgRightsText'];
                $data['lang'] = $GLOBALS['wgLanguageCode'];
 
                $data['rights'] = $GLOBALS['wgRightsText'];
                $data['lang'] = $GLOBALS['wgLanguageCode'];
 
-               $fallbackLang = $wgContLang->getFallbackLanguageCode();
-               $fallbackLangArray = array();
-               while( $fallbackLang ) {
-                       $fallbackLangArray[] = array( 'code' => $fallbackLang );
-                       $fallbackLang = Language::getFallbackFor( $fallbackLang );
+               $fallbacks = array();
+               foreach( $wgContLang->getFallbackLanguages() as $code ) {
+                       $fallbacks[] = array( 'code' => $code );
                }
                }
-               $data['fallback'] = $fallbackLangArray;
+               $data['fallback'] = $fallbacks;
                $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
 
                if ( $wgContLang->isRTL() ) {
                $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
 
                if ( $wgContLang->isRTL() ) {
index 46de0cd..474fde0 100644 (file)
@@ -148,9 +148,6 @@ class Language {
         * @return Language
         */
        protected static function newFromCode( $code ) {
         * @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 ) )
                // Protect against path traversal below
                if ( !Language::isValidCode( $code )
                        || strcspn( $code, ":/\\\000" ) !== strlen( $code ) )
@@ -166,35 +163,31 @@ class Language {
                        return $lang;
                }
 
                        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 = new $class;
+                       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::getFallbacksFor( $code );
+               foreach ( $fallbacks as $fallbackCode ) {
+                       if ( !Language::isValidBuiltInCode( $fallbackCode ) ) {
+                               throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
+                       }
 
 
-               if ( !MWInit::classExists( $class ) ) {
-                       $fallback = Language::getFallbackFor( $code );
-                       ++$recursionLevel;
-                       $lang = Language::newFromCode( $fallback );
-                       --$recursionLevel;
-                       $lang->setCode( $code );
-               } else {
-                       $lang = new $class;
+                       $class = self::classFromCode( $fallbackCode );
+                       self::preloadLanguageClass( $class );
+                       if ( MWInit::classExists( $class ) ) {
+                               $lang = Language::newFromCode( $fallbackCode );
+                               $lang->setCode( $code );
+                               return $lang;
+                       }
                }
                }
-               return $lang;
+
+               throw new MWException( "Invalid fallback sequence for language '$code'" );
        }
 
        /**
        }
 
        /**
@@ -225,6 +218,32 @@ class Language {
                return preg_match( '/^[a-z0-9-]*$/i', $code );
        }
 
                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
         *
        /**
         * Get the LocalisationCache instance
         *
@@ -266,14 +285,21 @@ class Language {
        function initContLang() { }
 
        /**
        function initContLang() { }
 
        /**
+        * Same as getFallbacksFor for current language.
         * @return array|bool
         * @return array|bool
+        * @deprecated in 1.19
         */
        function getFallbackLanguageCode() {
         */
        function getFallbackLanguageCode() {
-               if ( $this->mCode === 'en' ) {
-                       return false;
-               } else {
-                       return self::$dataCache->getItem( $this->mCode, 'fallback' );
-               }
+               wfDeprecated( __METHOD__ );
+               return self::getFallbackFor( $this->mCode );
+       }
+
+       /**
+        * @return array
+        * @since 1.19
+        */
+       function getFallbackLanguages() {
+               return self::getFallbacksFor( $this->mCode );
        }
 
        /**
        }
 
        /**
@@ -2437,15 +2463,7 @@ class Language {
         * @param $newWords array
         */
        function addMagicWordsByLang( $newWords ) {
         * @param $newWords array
         */
        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 = $this->getFallbackLanguages();
                $fallbackChain = array_reverse( $fallbackChain );
                foreach ( $fallbackChain as $code ) {
                        if ( isset( $newWords[$code] ) ) {
                $fallbackChain = array_reverse( $fallbackChain );
                foreach ( $fallbackChain as $code ) {
                        if ( isset( $newWords[$code] ) ) {
@@ -3294,7 +3312,7 @@ class Language {
        }
 
        /**
        }
 
        /**
-        * Get the fallback for a given language
+        * Get the first fallback for a given language
         *
         * @param $code string
         *
         *
         * @param $code string
         *
@@ -3302,10 +3320,31 @@ class Language {
         */
        static function getFallbackFor( $code ) {
                if ( $code === 'en' ) {
         */
        static function getFallbackFor( $code ) {
                if ( $code === 'en' ) {
-                       // Shortcut
                        return false;
                } else {
                        return false;
                } else {
-                       return self::getLocalisationCache()->getItem( $code, 'fallback' );
+                       $fallbacks = self::getFallbacksFor( $code );
+                       $first = array_shift( $fallbacks );
+                       return $first;
+               }
+       }
+
+       /**
+        * Get the ordered list of fallback languages.
+        *
+        * @since 1.19
+        * @param $code string Language code
+        * @return array
+        */
+       static function getFallbacksFor( $code ) {
+               if ( $code === 'en' ) {
+                       return array();
+               } else {
+                       $v = self::getLocalisationCache()->getItem( $code, 'fallback' );
+                       $v = array_map( 'trim', explode( ',', $v ) );
+                       if ( $v[count( $v ) - 1] !== 'en' ) {
+                               $v[] = 'en';
+                       }
+                       return $v;
                }
        }
 
                }
        }
 
index 604f49f..867d2a8 100644 (file)
@@ -9,4 +9,4 @@
  * @comment Deprecated code. Falls back to 'gsw'.
  */
 
  * @comment Deprecated code. Falls back to 'gsw'.
  */
 
-$fallback = 'gsw';
+$fallback = 'gsw, de';
index d32e58a..44041f0 100644 (file)
@@ -12,4 +12,4 @@
  *
  */
 
  *
  */
 
-$fallback = 'sgs';
+$fallback = 'sgs, lt';
index 85c3fd7..9407647 100644 (file)
@@ -9,4 +9,4 @@
  * @comment Deprecated language code. Falls back to 'vro'.
  */
 
  * @comment Deprecated language code. Falls back to 'vro'.
  */
 
-$fallback = 'vro';
+$fallback = 'vro, et';
index 4111735..3850261 100644 (file)
@@ -12,7 +12,7 @@
  * @author Vipuser
  */
 
  * @author Vipuser
  */
 
-$fallback = 'gan-hant';
+$fallback = 'gan-hant, zh-hant, zh-hans';
 
 $namespaceNames = array(
        NS_MEDIA            => 'Media',
 
 $namespaceNames = array(
        NS_MEDIA            => 'Media',
@@ -38,5 +38,4 @@ $messages = array(
 'variantname-gan-hans' => '简体',
 'variantname-gan-hant' => '繁體',
 'variantname-gan'      => '贛語原文',
 'variantname-gan-hans' => '简体',
 'variantname-gan-hant' => '繁體',
 'variantname-gan'      => '贛語原文',
-
 );
 );
index edd1fdb..39acc2c 100644 (file)
@@ -14,7 +14,7 @@
  * @author Vipuser
  */
 
  * @author Vipuser
  */
 
-$fallback = 'zh-hant';
+$fallback = 'zh-hant, zh-hans';
 
 $namespaceNames = array(
        NS_TALK             => '談詑',
 
 $namespaceNames = array(
        NS_TALK             => '談詑',
index 1dc08fe..a827db8 100644 (file)
  * @author Biŋhai
  */
 
  * @author Biŋhai
  */
 
-$fallback = 'zh-cn';
+$fallback = 'zh-cn, zh-hans';
 
 $messages = array(
 
 $messages = array(
-# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
 'mainpage' => 'ꀨꏾꌠ',
 'mainpage' => 'ꀨꏾꌠ',
-
 );
 );
index 7b812e2..b645de6 100644 (file)
@@ -14,7 +14,7 @@
  * @author Urhixidur
  */
 
  * @author Urhixidur
  */
 
-$fallback = 'kk-latn';
+$fallback = 'kk-latn, kk-cyrl';
 
 $separatorTransformTable = array(
        ',' => "\xc2\xa0",
 
 $separatorTransformTable = array(
        ',' => "\xc2\xa0",
index e96d890..fba4e9f 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
  */
 
 # Inherit everything for now
-$fallback = 'kk-arab';
+$fallback = 'kk-arab, kk-cyrl';
index 41d530a..05627f9 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
  */
 
 # Inherit everything for now
-$fallback = 'kk-latn';
+$fallback = 'kk-latn, kk-cyrl';
index 4276ae7..92befb8 100644 (file)
@@ -10,7 +10,7 @@
  * @author לערי ריינהארט
  */
 
  * @author לערי ריינהארט
  */
 
-$fallback = 'jv';
+$fallback = 'jv, id';
 
 $messages = array(
 'article'    => 'Isi tulisan',
 
 $messages = array(
 'article'    => 'Isi tulisan',
index 6f419da..2032bf4 100644 (file)
@@ -11,7 +11,7 @@
  * @author Sylvain2803
  */
 
  * @author Sylvain2803
  */
 
-$fallback = 'qu';
+$fallback = 'qu, es';
 
 $messages = array(
 # Dates
 
 $messages = array(
 # Dates
index d0d6cff..2545cb0 100644 (file)
@@ -13,7 +13,7 @@
  * @author Tkalyn
  */
 
  * @author Tkalyn
  */
 
-$fallback = 'uk';
+$fallback = 'uk, ru';
 
 $namespaceNames = array(
        NS_MEDIA            => 'Медіа',
 
 $namespaceNames = array(
        NS_MEDIA            => 'Медіа',
index d3d5283..ce2d0ce 100644 (file)
@@ -10,4 +10,4 @@
  *
  */
 
  *
  */
 
-$fallback = 'ruq-latn';
+$fallback = 'ruq-latn, ro';
index 6cfb678..1cc1166 100644 (file)
@@ -10,4 +10,4 @@
  * @comment Placeholder for Tatar. Falls back to Tatar in Latin script.
  */
 
  * @comment Placeholder for Tatar. Falls back to Tatar in Latin script.
  */
 
-$fallback = 'tt-cyrl';
+$fallback = 'tt-cyrl, ru';
index dc88925..70a3612 100644 (file)
@@ -17,7 +17,7 @@
  * @author Yuyu
  */
 
  * @author Yuyu
  */
 
-$fallback = 'zh-hant';
+$fallback = 'zh-hant, zh-hans';
 
 $fallback8bitEncoding = 'Big5-HKSCS';
 
 
 $fallback8bitEncoding = 'Big5-HKSCS';
 
index 6772ef0..37be152 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
  */
 
 # Inherit everything for now
-$fallback = 'zh-hk';
+$fallback = 'zh-hk, zh-hant, zh-hans';
index a27ed45..01b5524 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
  */
 
 # Inherit everything for now
-$fallback = 'zh-sg';
+$fallback = 'zh-sg, zh-hans';
index 5ecd229..55f6777 100644 (file)
@@ -22,6 +22,8 @@
  * @author לערי ריינהארט
  */
 
  * @author לערי ריינהארט
  */
 
+$fallback = 'zh-hant, zh-hans';
+
 $specialPageAliases = array(
        'Ancientpages'              => array( '最舊頁面' ),
        'Block'                     => array( '查封用戶' ),
 $specialPageAliases = array(
        'Ancientpages'              => array( '最舊頁面' ),
        'Block'                     => array( '查封用戶' ),
@@ -43,8 +45,6 @@ $specialPageAliases = array(
        'Withoutinterwiki'          => array( '沒有跨語言鏈接的頁面' ),
 );
 
        'Withoutinterwiki'          => array( '沒有跨語言鏈接的頁面' ),
 );
 
-$fallback = 'zh-hant';
-
 $namespaceNames = array(
        NS_USER             => '使用者',
        NS_USER_TALK        => '使用者討論',
 $namespaceNames = array(
        NS_USER             => '使用者',
        NS_USER_TALK        => '使用者討論',