3 require_once 'HTMLPurifier/Language.php';
4 require_once 'HTMLPurifier/AttrDef/Lang.php';
7 * Class responsible for generating HTMLPurifier_Language objects, managing
8 * caching and fallbacks.
9 * @note Thanks to MediaWiki for the general logic, although this version
10 * has been entirely rewritten
12 class HTMLPurifier_LanguageFactory
16 * Cache of language code information used to load HTMLPurifier_Language objects
17 * Structure is: $factory->cache[$language_code][$key] = $value
23 * Valid keys in the HTMLPurifier_Language object. Designates which
24 * variables to slurp out of a message file.
27 var $keys = array('fallback', 'messages');
30 * Instance of HTMLPurifier_AttrDef_Lang to validate language codes
31 * @value object HTMLPurifier_AttrDef_Lang
36 * Cached copy of dirname(__FILE__), directory of current file without
38 * @value string filename
43 * Keys whose contents are a hash map and can be merged
46 var $mergeable_keys_map = array('messages' => true);
49 * Keys whose contents are a list and can be merged
52 var $mergeable_keys_list = array();
55 * Retrieve sole instance of the factory.
57 * @param $prototype Optional prototype to overload sole instance with,
58 * or bool true to reset to default factory.
60 function &instance($prototype = null) {
61 static $instance = null;
62 if ($prototype !== null) {
63 $instance = $prototype;
64 } elseif ($instance === null ||
$prototype == true) {
65 $instance = new HTMLPurifier_LanguageFactory();
72 * Sets up the singleton, much like a constructor
73 * @note Prevents people from getting this outside of the singleton
76 $this->validator
= new HTMLPurifier_AttrDef_Lang();
77 $this->dir
= dirname(__FILE__
);
81 * Creates a language object, handles class fallbacks
82 * @param $code string language code
84 function create($code) {
86 $config = $context = false; // hope it doesn't use these!
87 $code = $this->validator
->validate($code, $config, $context);
88 if ($code === false) $code = 'en'; // malformed code becomes English
90 $pcode = str_replace('-', '_', $code); // make valid PHP classname
91 static $depth = 0; // recursion protection
94 $class = 'HTMLPurifier_Language';
95 $file = $this->dir
. '/Language.php';
97 $class = 'HTMLPurifier_Language_' . $pcode;
98 $file = $this->dir
. '/Language/classes/' . $code . '.php';
99 // PHP5/APC deps bug workaround can go here
100 // you can bypass the conditional include by loading the
102 if (file_exists($file) && !class_exists($class)) {
107 if (!class_exists($class)) {
109 $fallback = HTMLPurifier_Language
::getFallbackFor($code);
111 $lang = Language
::factory( $fallback );
123 * Returns the fallback language for language
124 * @note Loads the original language into cache
125 * @param $code string language code
127 function getFallbackFor($code) {
128 $this->loadLanguage($code);
129 return $this->cache
[$code]['fallback'];
133 * Loads language into the cache, handles message file and fallbacks
134 * @param $code string language code
136 function loadLanguage($code) {
137 static $languages_seen = array(); // recursion guard
139 // abort if we've already loaded it
140 if (isset($this->cache
[$code])) return;
143 $filename = $this->dir
. '/Language/messages/' . $code . '.php';
145 // default fallback : may be overwritten by the ensuing include
146 $fallback = ($code != 'en') ?
'en' : false;
148 // load primary localisation
149 if (!file_exists($filename)) {
150 // skip the include: will rely solely on fallback
151 $filename = $this->dir
. '/Language/messages/en.php';
155 $cache = compact($this->keys
);
158 // load fallback localisation
159 if (!empty($fallback)) {
161 // infinite recursion guard
162 if (isset($languages_seen[$code])) {
163 trigger_error('Circular fallback reference in language ' .
164 $code, E_USER_ERROR
);
167 $language_seen[$code] = true;
169 // load the fallback recursively
170 $this->loadLanguage($fallback);
171 $fallback_cache = $this->cache
[$fallback];
173 // merge fallback with current language
174 foreach ( $this->keys
as $key ) {
175 if (isset($cache[$key]) && isset($fallback_cache[$key])) {
176 if (isset($this->mergeable_keys_map
[$key])) {
177 $cache[$key] = $cache[$key] +
$fallback_cache[$key];
178 } elseif (isset($this->mergeable_keys_list
[$key])) {
179 $cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] );
182 $cache[$key] = $fallback_cache[$key];
188 // save to cache for later retrieval
189 $this->cache
[$code] = $cache;