Index: trunk/phase3/includes/Message.php |
— | — | @@ -1,5 +1,4 @@ |
2 | 2 | <?php |
3 | | - |
4 | 3 | /** |
5 | 4 | * OBS!!! *EXPERIMENTAL* This class is still under discussion. |
6 | 5 | * |
— | — | @@ -11,34 +10,37 @@ |
12 | 11 | * |
13 | 12 | * Examples: |
14 | 13 | * Fetching a message text for interface message |
15 | | - * $button = Xml::button( Message::key( 'submit' )->text() ); |
| 14 | + * $button = Xml::button( Message::key( 'submit' )->text() ); |
16 | 15 | * Messages can have parameters: |
17 | | - * Message::key( 'welcome-to' )->param( $wgSitename )->text(); // {{GRAMMAR}} and friends work correctly |
18 | | - * Message::key( 'are-friends' )->params( $user, $friend ); |
19 | | - * Message::key( 'bad-message' )->rawParam( '<script>...</script>' )->escaped() |
| 16 | + * Message::key( 'welcome-to' )->params( $wgSitename )->text(); |
| 17 | + * {{GRAMMAR}} and friends work correctly |
| 18 | + * Message::key( 'are-friends' )->params( $user, $friend ); |
| 19 | + * Message::key( 'bad-message' )->rawParams( '<script>...</script>' )->escaped() |
20 | 20 | * Sometimes the message text ends up in the database, so content language is needed. |
21 | | - * Message::key( 'file-log' )->params( $user, $filename )->inContentLanguage()->text() |
| 21 | + * Message::key( 'file-log' )->params( $user, $filename )->inContentLanguage()->text() |
22 | 22 | * Checking if message exists: |
23 | | - * Message::key( 'mysterious-message' )->exists() |
| 23 | + * Message::key( 'mysterious-message' )->exists() |
24 | 24 | * If you want to use a different language: |
25 | | - * Message::key( 'email-header' )->language( $user->getOption( 'language' ) )->plain() |
| 25 | + * Message::key( 'email-header' )->language( $user->getOption( 'language' ) )->plain() |
| 26 | + * Note that you cannot parse the text except in the content or interface |
| 27 | + * languages |
26 | 28 | * |
27 | 29 | * |
28 | 30 | * Comparison with old wfMsg* functions: |
29 | 31 | * |
30 | 32 | * Use full parsing. |
31 | | - * Would correspond to wfMsgExt( 'key', array( 'parseinline' ), 'apple' ); |
32 | | - * $parsed = Message::key( 'key' )->param( 'apple' )->parse(); |
| 33 | + * wfMsgExt( 'key', array( 'parseinline' ), 'apple' ); |
| 34 | + * === Message::key( 'key' )->params( 'apple' )->parse(); |
33 | 35 | * Parseinline is used because it is more useful when pre-building html. |
34 | 36 | * In normal use it is better to use OutputPage::(add|wrap)WikiMsg. |
35 | 37 | * |
36 | 38 | * Places where html cannot be used. {{-transformation is done. |
37 | | - * Would correspond to wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' ); |
38 | | - * $plain = Message::key( 'key' )->params( 'apple', 'pear' )->text(); |
| 39 | + * wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' ); |
| 40 | + * === Message::key( 'key' )->params( 'apple', 'pear' )->text(); |
39 | 41 | * |
40 | 42 | * Shortcut for escaping the message too, similar to wfMsgHTML, but |
41 | 43 | * parameters are not replaced after escaping by default. |
42 | | - * $escaped = Message::key( 'key' )->rawParam( 'apple' )->escaped(); |
| 44 | + * $escaped = Message::key( 'key' )->rawParams( 'apple' )->escaped(); |
43 | 45 | * |
44 | 46 | * TODO: |
45 | 47 | * * test, can we have tests? |
— | — | @@ -53,11 +55,13 @@ |
54 | 56 | * means the current interface language, false content language. |
55 | 57 | */ |
56 | 58 | protected $interface = true; |
| 59 | + |
57 | 60 | /** |
58 | 61 | * In which language to get this message. Overrides the $interface |
59 | 62 | * variable. |
60 | 63 | */ |
61 | 64 | protected $language = null; |
| 65 | + |
62 | 66 | /** |
63 | 67 | * The message key. |
64 | 68 | */ |
— | — | @@ -67,14 +71,39 @@ |
68 | 72 | * List of parameters which will be substituted into the message. |
69 | 73 | */ |
70 | 74 | protected $parameters = array(); |
| 75 | + |
| 76 | + /** |
| 77 | + * Some situations need exotic combinations of options to the |
| 78 | + * underlying Language modules; which can be specified here. |
| 79 | + * Dependencies: |
| 80 | + * 'parse' implies 'transform', 'escape' |
| 81 | + */ |
| 82 | + protected $options = array( |
| 83 | + # Don't wrap the output in a block-level element |
| 84 | + 'inline' => true, |
| 85 | + # Expand {{ constructs |
| 86 | + 'transform' => true, |
| 87 | + # Output will be safe HTML |
| 88 | + 'escape' => true, |
| 89 | + # Parse the text with the full parser |
| 90 | + 'parse' => true, |
| 91 | + # Access the database when getting the message text |
| 92 | + 'usedb' => true, |
| 93 | + ); |
71 | 94 | |
72 | 95 | /** |
73 | 96 | * Constructor. |
74 | 97 | * @param $key String: message key |
75 | 98 | * @return Message: $this |
76 | 99 | */ |
77 | | - public function __construct( $key ) { |
| 100 | + public function __construct( $key, $params=array(), $options=array() ) { |
78 | 101 | $this->key = $key; |
| 102 | + if( $params ){ |
| 103 | + $this->params( $params ); |
| 104 | + } |
| 105 | + if( $options ){ |
| 106 | + $this->options( $options ); |
| 107 | + } |
79 | 108 | } |
80 | 109 | |
81 | 110 | /** |
— | — | @@ -91,44 +120,42 @@ |
92 | 121 | |
93 | 122 | /** |
94 | 123 | * Adds parameters to the parameter list of this message. |
95 | | - * @param $value String: parameter |
| 124 | + * @params Vargars: parameters as Strings |
96 | 125 | * @return Message: $this |
97 | 126 | */ |
98 | | - public function param( $value ) { |
99 | | - $this->parameters[] = $value; |
100 | | - return $this; |
101 | | - } |
102 | | - |
103 | | - /** |
104 | | - * Adds parameters to the parameter list of this message. |
105 | | - * @params Vargars: parameters |
106 | | - * @return Message: $this |
107 | | - */ |
108 | 127 | public function params( /*...*/ ) { |
109 | | - $this->paramList( func_get_args() ); |
| 128 | + $this->parameters += array_values( func_get_args() ); |
110 | 129 | return $this; |
111 | 130 | } |
112 | 131 | |
113 | 132 | /** |
114 | | - * Adds a list of parameters to the parameter list of this message. |
115 | | - * @param $value Array: list of parameters, array keys will be ignored. |
| 133 | + * Add parameters that are substituted after parsing or escaping. |
| 134 | + * In other words the parsing process cannot access the contents |
| 135 | + * of this type of parameter, and you need to make sure it is |
| 136 | + * sanitized beforehand. The parser will see "$n", instead. |
| 137 | + * @param $value Varargs: raw parameters as Strings |
116 | 138 | * @return Message: $this |
117 | 139 | */ |
118 | | - public function paramList( array $values ) { |
119 | | - $this->parameters += array_values( $values ); |
| 140 | + public function rawParams( /*...*/ ) { |
| 141 | + $params = func_get_args(); |
| 142 | + foreach( $params as $param ){ |
| 143 | + $this->parameters[] = array( 'raw' => $param ); |
| 144 | + } |
120 | 145 | return $this; |
121 | 146 | } |
122 | | - |
| 147 | + |
123 | 148 | /** |
124 | | - * Adds a parameters that is substituted after parsing or escaping. |
125 | | - * In other words the parsing process cannot access the contents |
126 | | - * of this type parameter, and you need to make sure it is |
127 | | - * sanitized beforehand. |
128 | | - * @param $value String: raw parameter |
129 | | - * @return Message: $this |
| 149 | + * Set some of the individual options, if you need to use some |
| 150 | + * funky combination of them. |
| 151 | + * @param $options Array $option => $value |
| 152 | + * @return Message $this |
130 | 153 | */ |
131 | | - public function rawParam( $value ) { |
132 | | - $this->parameters[] = array( 'raw' => $value ); |
| 154 | + public function options( array $options ){ |
| 155 | + foreach( $options as $key => $value ){ |
| 156 | + if( in_array( $key, $this->options ) ){ |
| 157 | + $this->options[$key] = (bool)$value; |
| 158 | + } |
| 159 | + } |
133 | 160 | return $this; |
134 | 161 | } |
135 | 162 | |
— | — | @@ -139,11 +166,16 @@ |
140 | 167 | * @param $lang Mixed: langauge code or language object. |
141 | 168 | * @return Message: $this |
142 | 169 | */ |
143 | | - public function language( Language $lang ) { |
144 | | - if ( is_string( $lang ) ) { |
| 170 | + public function language( $lang ) { |
| 171 | + if( $lang instanceof Language ){ |
| 172 | + $this->language = $lang; |
| 173 | + } elseif ( is_string( $lang ) ) { |
145 | 174 | $this->language = Language::factory( $lang ); |
146 | 175 | } else { |
147 | | - $this->language = $lang; |
| 176 | + $type = gettype( $lang ); |
| 177 | + throw new MWException( "Message::langauge() must be " |
| 178 | + . "passed a String or Language object; $type given" |
| 179 | + ); |
148 | 180 | } |
149 | 181 | $this->interface = false; |
150 | 182 | return $this; |
— | — | @@ -161,27 +193,75 @@ |
162 | 194 | |
163 | 195 | /** |
164 | 196 | * Returns the message parsed from wikitext to HTML. |
| 197 | + * TODO: in PHP >= 5.2.0, we can make this a magic method, |
| 198 | + * and then we can do, eg: |
| 199 | + * $foo = Message::get($key); |
| 200 | + * $string = "<abbr>$foo</abbr>"; |
| 201 | + * But we shouldn't implement that while MediaWiki still supports |
| 202 | + * PHP < 5.2; or people will start using it... |
165 | 203 | * @return String: HTML |
166 | 204 | */ |
167 | | - public function parse() { |
168 | | - $string = $this->parseAsBlock( $string ); |
169 | | - $m = array(); |
170 | | - if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { |
171 | | - $string = $m[1]; |
| 205 | + public function toString() { |
| 206 | + $string = $this->getMessageText(); |
| 207 | + |
| 208 | + # Replace parameters before text parsing |
| 209 | + $string = $this->replaceParameters( $string, 'before' ); |
| 210 | + |
| 211 | + # Maybe transform using the full parser |
| 212 | + if( $this->options['parse'] ){ |
| 213 | + $string = $this->parseText( $string ); |
| 214 | + } else { |
| 215 | + |
| 216 | + # Transform {{ constructs |
| 217 | + if( $this->options['transform'] ){ |
| 218 | + $string = $this->transformText( $string ); |
| 219 | + } |
| 220 | + |
| 221 | + # Sanitise |
| 222 | + if( $this->options['escape'] ){ |
| 223 | + # FIXME: Sanitizer method here? |
| 224 | + $string = htmlspecialchars( $string ); |
| 225 | + } |
172 | 226 | } |
| 227 | + |
| 228 | + # Strip the block element |
| 229 | + if( !$this->options['inline'] ){ |
| 230 | + $m = array(); |
| 231 | + if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { |
| 232 | + $string = $m[1]; |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + # Raw parameter replacement |
| 237 | + $string = $this->replaceParameters( $string, 'after' ); |
| 238 | + |
173 | 239 | return $string; |
174 | 240 | } |
| 241 | + |
| 242 | + public function __tostring(){ return $this->toString(); } |
| 243 | + |
| 244 | + /** |
| 245 | + * Fully parse the text from wikitext to HTML |
| 246 | + * @return String parsed HTML |
| 247 | + */ |
| 248 | + public function parse(){ |
| 249 | + $this->options( array( |
| 250 | + 'parse' => true, |
| 251 | + )); |
| 252 | + return $this->tostring(); |
| 253 | + } |
175 | 254 | |
176 | 255 | /** |
177 | 256 | * Returns the message text. {{-transformation is done. |
178 | 257 | * @return String: Unescaped message text. |
179 | 258 | */ |
180 | 259 | public function text() { |
181 | | - $string = $this->getMessageText(); |
182 | | - $string = $this->replaceParameters( 'before' ); |
183 | | - $string = $this->transformText( $string ); |
184 | | - $string = $this->replaceParameters( 'after' ); |
185 | | - return $string; |
| 260 | + $this->options( array( |
| 261 | + 'parse' => false, |
| 262 | + 'transform' => true, |
| 263 | + 'escape' => false, |
| 264 | + )); |
| 265 | + return $this->tostring(); |
186 | 266 | } |
187 | 267 | |
188 | 268 | /** |
— | — | @@ -189,10 +269,13 @@ |
190 | 270 | * @return String: Unescaped untransformed message text. |
191 | 271 | */ |
192 | 272 | public function plain() { |
193 | | - $string = $this->getMessageText(); |
194 | | - $string = $this->replaceParameters( 'before' ); |
195 | | - $string = $this->replaceParameters( 'after' ); |
196 | | - return $string; |
| 273 | + $this->options( array( |
| 274 | + 'parse' => false, |
| 275 | + 'transform' => false, |
| 276 | + 'escape' => false, |
| 277 | + 'inline' => false, |
| 278 | + )); |
| 279 | + return $this->tostring(); |
197 | 280 | } |
198 | 281 | |
199 | 282 | /** |
— | — | @@ -200,11 +283,11 @@ |
201 | 284 | * @return String: HTML |
202 | 285 | */ |
203 | 286 | public function parseAsBlock() { |
204 | | - $string = $this->getMessageText(); |
205 | | - $string = $this->replaceParameters( 'before' ); |
206 | | - $string = $this->parseText( $string ); |
207 | | - $string = $this->replaceParameters( 'after' ); |
208 | | - return $string; |
| 287 | + $this->options( array( |
| 288 | + 'parse' => true, |
| 289 | + 'inline' => true, |
| 290 | + )); |
| 291 | + return $this->tostring(); |
209 | 292 | } |
210 | 293 | |
211 | 294 | /** |
— | — | @@ -213,12 +296,13 @@ |
214 | 297 | * @return String: Escaped message text. |
215 | 298 | */ |
216 | 299 | public function escaped() { |
217 | | - $string = $this->getMessageText(); |
218 | | - $string = $this->replaceParameters( 'before' ); |
219 | | - $string = $this->transformText( $string ); |
220 | | - $string = htmlspecialchars( $string ); |
221 | | - $string = $this->replaceParameters( 'after' ); |
222 | | - return $string; |
| 300 | + $this->options( array( |
| 301 | + 'parse' => false, |
| 302 | + 'transform' => true, |
| 303 | + 'escape' => true, |
| 304 | + 'inline' => false, |
| 305 | + )); |
| 306 | + return $this->tostring(); |
223 | 307 | } |
224 | 308 | |
225 | 309 | /** |
— | — | @@ -226,18 +310,24 @@ |
227 | 311 | * @return Bool: true if it is and false if not. |
228 | 312 | */ |
229 | 313 | public function exists() { |
230 | | - return !wfEmptyMsg( $this->key, $this->getMessageText() ); |
| 314 | + global $wgMessageCache; |
| 315 | + return $wgMessageCache->get( |
| 316 | + $this->key, |
| 317 | + $this->options['usedb'], |
| 318 | + $this->language |
| 319 | + ) !== false; |
231 | 320 | } |
232 | 321 | |
233 | 322 | /** |
234 | 323 | * Substitutes any paramaters into the message text. |
| 324 | + * @param $message String, the message text |
235 | 325 | * @param $type String: either before or after |
236 | 326 | * @return String |
237 | 327 | */ |
238 | | - protected function replaceParameters( $type = 'before' ) { |
| 328 | + protected function replaceParameters( $message, $type = 'before' ) { |
239 | 329 | $replacementKeys = array(); |
240 | | - foreach( $args as $n => $param ) { |
241 | | - if ( $type === 'before' && !isset( $param['raw'] ) ) { |
| 330 | + foreach( $this->parameters as $n => $param ) { |
| 331 | + if ( $type === 'before' && !is_array( $param ) ) { |
242 | 332 | $replacementKeys['$' . ($n + 1)] = $param; |
243 | 333 | } elseif ( $type === 'after' && isset( $param['raw'] ) ) { |
244 | 334 | $replacementKeys['$' . ($n + 1)] = $param['raw']; |
— | — | @@ -255,10 +345,10 @@ |
256 | 346 | protected function parseText( $string ) { |
257 | 347 | global $wgOut; |
258 | 348 | if ( $this->language !== null ) { |
259 | | - // FIXME: remove this limitation |
| 349 | + # FIXME: remove this limitation |
260 | 350 | throw new MWException( 'Can only parse in interface or content language' ); |
261 | 351 | } |
262 | | - return $wgOut->parse( $string, /*linestart*/true, $this->interface() ); |
| 352 | + return $wgOut->parse( $string, /*linestart*/true, $this->interface ); |
263 | 353 | } |
264 | 354 | |
265 | 355 | /** |
— | — | @@ -277,7 +367,10 @@ |
278 | 368 | */ |
279 | 369 | protected function getMessageText() { |
280 | 370 | global $wgMessageCache; |
281 | | - return $wgMessageCache->get( $this->key, /*DB*/true, $this->language ); |
| 371 | + $message = $wgMessageCache->get( $this->key, $this->options['usedb'], $this->language ); |
| 372 | + return $message === false |
| 373 | + ? '<' . htmlspecialchars( $this->key ) . '>' |
| 374 | + : $message; |
282 | 375 | } |
283 | 376 | |
284 | 377 | } |
\ No newline at end of file |