This is PHP, updating docs to reflect that
[lhc/web/wiklou.git] / includes / Message.php
1 <?php
2
3 /**
4 * OBS!!! *EXPERIMENTAL* This class is still under discussion.
5 *
6 * This class provides methods for fetching interface messages and
7 * processing them into variety of formats that are needed in MediaWiki.
8 *
9 * It is intented to replace the old wfMsg* functions that over time grew
10 * unusable.
11 *
12 * Examples:
13 * Fetching a message text for interface message
14 * $button = Xml::button( Message::key( 'submit' )->text() );
15 * Messages can have parameters:
16 * Message::key( 'welcome-to' )->param( $wgSitename )->text(); // {{GRAMMAR}} and friends work correctly
17 * Message::key( 'are-friends' )->params( $user, $friend );
18 * Message::key( 'bad-message' )->rawParam( '<script>...</script>' )->escaped()
19 * Sometimes the message text ends up in the database, so content language is needed.
20 * Message::key( 'file-log' )->params( $user, $filename )->inContentLanguage()->text()
21 * Checking if message exists:
22 * Message::key( 'mysterious-message' )->exists()
23 * If you want to use a different language:
24 * Message::key( 'email-header' )->language( $user->getOption( 'language' ) )->plain()
25 *
26 *
27 * Comparison with old wfMsg* functions:
28 *
29 * Use full parsing.
30 * Would correspond to wfMsgExt( 'key', array( 'parseinline' ), 'apple' );
31 * $parsed = Message::key( 'key' )->param( 'apple' )->parse();
32 * Parseinline is used because it is more useful when pre-building html.
33 * In normal use it is better to use OutputPage::(add|wrap)WikiMsg.
34 *
35 * Places where html cannot be used. {{-transformation is done.
36 * Would correspond to wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' );
37 * $plain = Message::key( 'key' )->params( 'apple', 'pear' )->text();
38 *
39 * Shortcut for escaping the message too, similar to wfMsgHTML, but
40 * parameters are not replaced after escaping by default.
41 * $escaped = Message::key( 'key' )->rawParam( 'apple' )->escaped();
42 *
43 * TODO:
44 * * test, can we have tests?
45 * * sort out the details marked with fixme
46 * * should we have _m() or similar global wrapper?
47 *
48 * @since 1.17
49 */
50 class Message {
51 /**
52 * In which language to get this message. True, which is the default,
53 * means the current interface language, false content language.
54 */
55 protected $interface = true;
56 /**
57 * In which language to get this message. Overrides the $interface
58 * variable.
59 */
60 protected $language = null;
61 /**
62 * The message key.
63 */
64 protected $key;
65
66 /**
67 * List of parameters which will be substituted into the message.
68 */
69 protected $parameters = array();
70
71 /**
72 * Constructor.
73 * @param $key String: message key
74 * @return Message: $this
75 */
76 public function __construct( $key ) {
77 $this->key = $key;
78 }
79
80 /**
81 * Factory function that is just wrapper for the real constructor. It is
82 * intented to be used instead of the real constructor, because it allows
83 * chaining method calls, while new objects don't.
84 * //FIXME: key or get or something else?
85 * @param $key String: message key
86 * @return Message: $this
87 */
88 public function key( $key ) {
89 return new Message( $key );
90 }
91
92 /**
93 * Adds parameters to the parameter list of this message.
94 * @param $value String: parameter
95 * @return Message: $this
96 */
97 public function param( $value ) {
98 $this->parameters[] = $value;
99 return $this;
100 }
101
102 /**
103 * Adds parameters to the parameter list of this message.
104 * @params Vargars: parameters
105 * @return Message: $this
106 */
107 public function params( /*...*/ ) {
108 $this->paramList( func_get_args() );
109 return $this;
110 }
111
112 /**
113 * Adds a list of parameters to the parameter list of this message.
114 * @param $value Array: list of parameters, array keys will be ignored.
115 * @return Message: $this
116 */
117 public function paramList( array $values ) {
118 $this->parameters += array_values( $values );
119 return $this;
120 }
121
122 /**
123 * Adds a parameters that is substituted after parsing or escaping.
124 * In other words the parsing process cannot access the contents
125 * of this type parameter, and you need to make sure it is
126 * sanitized beforehand.
127 * @param $value String: raw parameter
128 * @return Message: $this
129 */
130 public function rawParam( $value ) {
131 $this->parameters[] = array( 'raw' => $value );
132 return $this;
133 }
134
135 /**
136 * Request the message in any language that is supported.
137 * As a side effect interface message status is unconditionally
138 * turned off.
139 * @param $lang Mixed: langauge code or language object.
140 * @return Message: $this
141 */
142 public function language( Language $lang ) {
143 if ( is_string( $lang ) ) {
144 $this->language = Language::factory( $lang );
145 } else {
146 $this->language = $lang;
147 }
148 $this->interface = false;
149 return $this;
150 }
151
152 /**
153 * Request the message in the wiki's content language.
154 * @return Message: $this
155 */
156 public function inContentLanguage() {
157 $this->interface = false;
158 $this->language = null;
159 return $this;
160 }
161
162 /**
163 * Returns the message parsed from wikitext to HTML.
164 * @return String: HTML
165 */
166 public function parse() {
167 $string = $this->parseAsBlock( $string );
168 $m = array();
169 if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
170 $string = $m[1];
171 }
172 return $string;
173 }
174
175 /**
176 * Returns the message text. {{-transformation is done.
177 * @return String: Unescaped message text.
178 */
179 public function text() {
180 $string = $this->getMessageText();
181 $string = $this->replaceParameters( 'before' );
182 $string = $this->transformText( $string );
183 $string = $this->replaceParameters( 'after' );
184 return $string;
185 }
186
187 /**
188 * Returns the message text as-is, only parameters are subsituted.
189 * @return String: Unescaped untransformed message text.
190 */
191 public function plain() {
192 $string = $this->getMessageText();
193 $string = $this->replaceParameters( 'before' );
194 $string = $this->replaceParameters( 'after' );
195 return $string;
196 }
197
198 /**
199 * Returns the parsed message text which is always surrounded by a block element.
200 * @return String: HTML
201 */
202 public function parseAsBlock() {
203 $string = $this->getMessageText();
204 $string = $this->replaceParameters( 'before' );
205 $string = $this->parseText( $string );
206 $string = $this->replaceParameters( 'after' );
207 return $string;
208 }
209
210 /**
211 * Returns the message text. {{-transformation is done and the result
212 * is excaped excluding any raw parameters.
213 * @return String: Escaped message text.
214 */
215 public function escaped() {
216 $string = $this->getMessageText();
217 $string = $this->replaceParameters( 'before' );
218 $string = $this->transformText( $string );
219 $string = htmlspecialchars( $string );
220 $string = $this->replaceParameters( 'after' );
221 return $string;
222 }
223
224 /**
225 * Check whether a message key has been defined currently.
226 * @return Bool: true if it is and false if not.
227 */
228 public function exists() {
229 return !wfEmptyMsg( $this->key, $this->getMessageText() );
230 }
231
232 /**
233 * Substitutes any paramaters into the message text.
234 * @param $type String: either before or after
235 * @return String
236 */
237 protected function replaceParameters( $type = 'before' ) {
238 $replacementKeys = array();
239 foreach( $args as $n => $param ) {
240 if ( $type === 'before' && !isset( $param['raw'] ) ) {
241 $replacementKeys['$' . ($n + 1)] = $param;
242 } elseif ( $type === 'after' && isset( $param['raw'] ) ) {
243 $replacementKeys['$' . ($n + 1)] = $param['raw'];
244 }
245 }
246 $message = strtr( $message, $replacementKeys );
247 return $message;
248 }
249
250 /**
251 * Wrapper for what ever method we use to parse wikitext.
252 * @param $string String: Wikitext message contents
253 * @return Wikitext parsed into HTML
254 */
255 protected function parseText( $string ) {
256 global $wgOut;
257 if ( $this->language !== null ) {
258 // FIXME: remove this limitation
259 throw new MWException( 'Can only parse in interface or content language' );
260 }
261 return $wgOut->parse( $string, /*linestart*/true, $this->interface() );
262 }
263
264 /**
265 * Wrapper for what ever method we use to {{-transform wikitext.
266 * @param $string String: Wikitext message contents
267 * @return Wikitext with {{-constructs replaced with their values.
268 */
269 protected function transformText( $string ) {
270 global $wgMessageCache;
271 return $wgMessageCache->transform( $string, $this->interface, $this->language );
272 }
273
274 /**
275 * Wrapper for what ever method we use to get message contents
276 * @return Unmodified message contents
277 */
278 protected function getMessageText() {
279 global $wgMessageCache;
280 return $wgMessageCache->get( $this->key, /*DB*/true, $this->language );
281 }
282
283 }