Yaffs site version 1.1
[yaffs-website] / vendor / ezyang / htmlpurifier / library / HTMLPurifier / LanguageFactory.php
1 <?php
2
3 /**
4  * Class responsible for generating HTMLPurifier_Language objects, managing
5  * caching and fallbacks.
6  * @note Thanks to MediaWiki for the general logic, although this version
7  *       has been entirely rewritten
8  * @todo Serialized cache for languages
9  */
10 class HTMLPurifier_LanguageFactory
11 {
12
13     /**
14      * Cache of language code information used to load HTMLPurifier_Language objects.
15      * Structure is: $factory->cache[$language_code][$key] = $value
16      * @type array
17      */
18     public $cache;
19
20     /**
21      * Valid keys in the HTMLPurifier_Language object. Designates which
22      * variables to slurp out of a message file.
23      * @type array
24      */
25     public $keys = array('fallback', 'messages', 'errorNames');
26
27     /**
28      * Instance to validate language codes.
29      * @type HTMLPurifier_AttrDef_Lang
30      *
31      */
32     protected $validator;
33
34     /**
35      * Cached copy of dirname(__FILE__), directory of current file without
36      * trailing slash.
37      * @type string
38      */
39     protected $dir;
40
41     /**
42      * Keys whose contents are a hash map and can be merged.
43      * @type array
44      */
45     protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
46
47     /**
48      * Keys whose contents are a list and can be merged.
49      * @value array lookup
50      */
51     protected $mergeable_keys_list = array();
52
53     /**
54      * Retrieve sole instance of the factory.
55      * @param HTMLPurifier_LanguageFactory $prototype Optional prototype to overload sole instance with,
56      *                   or bool true to reset to default factory.
57      * @return HTMLPurifier_LanguageFactory
58      */
59     public static function instance($prototype = null)
60     {
61         static $instance = null;
62         if ($prototype !== null) {
63             $instance = $prototype;
64         } elseif ($instance === null || $prototype == true) {
65             $instance = new HTMLPurifier_LanguageFactory();
66             $instance->setup();
67         }
68         return $instance;
69     }
70
71     /**
72      * Sets up the singleton, much like a constructor
73      * @note Prevents people from getting this outside of the singleton
74      */
75     public function setup()
76     {
77         $this->validator = new HTMLPurifier_AttrDef_Lang();
78         $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
79     }
80
81     /**
82      * Creates a language object, handles class fallbacks
83      * @param HTMLPurifier_Config $config
84      * @param HTMLPurifier_Context $context
85      * @param bool|string $code Code to override configuration with. Private parameter.
86      * @return HTMLPurifier_Language
87      */
88     public function create($config, $context, $code = false)
89     {
90         // validate language code
91         if ($code === false) {
92             $code = $this->validator->validate(
93                 $config->get('Core.Language'),
94                 $config,
95                 $context
96             );
97         } else {
98             $code = $this->validator->validate($code, $config, $context);
99         }
100         if ($code === false) {
101             $code = 'en'; // malformed code becomes English
102         }
103
104         $pcode = str_replace('-', '_', $code); // make valid PHP classname
105         static $depth = 0; // recursion protection
106
107         if ($code == 'en') {
108             $lang = new HTMLPurifier_Language($config, $context);
109         } else {
110             $class = 'HTMLPurifier_Language_' . $pcode;
111             $file  = $this->dir . '/Language/classes/' . $code . '.php';
112             if (file_exists($file) || class_exists($class, false)) {
113                 $lang = new $class($config, $context);
114             } else {
115                 // Go fallback
116                 $raw_fallback = $this->getFallbackFor($code);
117                 $fallback = $raw_fallback ? $raw_fallback : 'en';
118                 $depth++;
119                 $lang = $this->create($config, $context, $fallback);
120                 if (!$raw_fallback) {
121                     $lang->error = true;
122                 }
123                 $depth--;
124             }
125         }
126         $lang->code = $code;
127         return $lang;
128     }
129
130     /**
131      * Returns the fallback language for language
132      * @note Loads the original language into cache
133      * @param string $code language code
134      * @return string|bool
135      */
136     public function getFallbackFor($code)
137     {
138         $this->loadLanguage($code);
139         return $this->cache[$code]['fallback'];
140     }
141
142     /**
143      * Loads language into the cache, handles message file and fallbacks
144      * @param string $code language code
145      */
146     public function loadLanguage($code)
147     {
148         static $languages_seen = array(); // recursion guard
149
150         // abort if we've already loaded it
151         if (isset($this->cache[$code])) {
152             return;
153         }
154
155         // generate filename
156         $filename = $this->dir . '/Language/messages/' . $code . '.php';
157
158         // default fallback : may be overwritten by the ensuing include
159         $fallback = ($code != 'en') ? 'en' : false;
160
161         // load primary localisation
162         if (!file_exists($filename)) {
163             // skip the include: will rely solely on fallback
164             $filename = $this->dir . '/Language/messages/en.php';
165             $cache = array();
166         } else {
167             include $filename;
168             $cache = compact($this->keys);
169         }
170
171         // load fallback localisation
172         if (!empty($fallback)) {
173
174             // infinite recursion guard
175             if (isset($languages_seen[$code])) {
176                 trigger_error(
177                     'Circular fallback reference in language ' .
178                     $code,
179                     E_USER_ERROR
180                 );
181                 $fallback = 'en';
182             }
183             $language_seen[$code] = true;
184
185             // load the fallback recursively
186             $this->loadLanguage($fallback);
187             $fallback_cache = $this->cache[$fallback];
188
189             // merge fallback with current language
190             foreach ($this->keys as $key) {
191                 if (isset($cache[$key]) && isset($fallback_cache[$key])) {
192                     if (isset($this->mergeable_keys_map[$key])) {
193                         $cache[$key] = $cache[$key] + $fallback_cache[$key];
194                     } elseif (isset($this->mergeable_keys_list[$key])) {
195                         $cache[$key] = array_merge($fallback_cache[$key], $cache[$key]);
196                     }
197                 } else {
198                     $cache[$key] = $fallback_cache[$key];
199                 }
200             }
201         }
202
203         // save to cache for later retrieval
204         $this->cache[$code] = $cache;
205         return;
206     }
207 }
208
209 // vim: et sw=4 sts=4