summaryrefslogtreecommitdiff
path: root/includes/Message.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/Message.php')
-rw-r--r--includes/Message.php367
1 files changed, 323 insertions, 44 deletions
diff --git a/includes/Message.php b/includes/Message.php
index b0147b9a..57c6264d 100644
--- a/includes/Message.php
+++ b/includes/Message.php
@@ -22,11 +22,11 @@
*/
/**
- * The Message class provides methods which fullfil two basic services:
+ * The Message class provides methods which fulfil two basic services:
* - fetching interface messages
* - processing messages into a variety of formats
*
- * First implemented with MediaWiki 1.17, the Message class is intented to
+ * First implemented with MediaWiki 1.17, the Message class is intended to
* replace the old wfMsg* functions that over time grew unusable.
* @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences
* between old and new functions.
@@ -203,6 +203,11 @@ class Message {
protected $title = null;
/**
+ * Content object representing the message
+ */
+ protected $content = null;
+
+ /**
* @var string
*/
protected $message;
@@ -211,7 +216,7 @@ class Message {
* Constructor.
* @since 1.17
* @param $key: message key, or array of message keys to try and use the first non-empty message for
- * @param $params Array message parameters
+ * @param array $params message parameters
* @return Message: $this
*/
public function __construct( $key, $params = array() ) {
@@ -222,11 +227,50 @@ class Message {
}
/**
+ * Returns the message key
+ *
+ * @since 1.21
+ *
+ * @return string
+ */
+ public function getKey() {
+ if ( is_array( $this->key ) ) {
+ // May happen if some kind of fallback is applied.
+ // For now, just use the first key. We really need a better solution.
+ return $this->key[0];
+ } else {
+ return $this->key;
+ }
+ }
+
+ /**
+ * Returns the message parameters
+ *
+ * @since 1.21
+ *
+ * @return string[]
+ */
+ public function getParams() {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns the message format
+ *
+ * @since 1.21
+ *
+ * @return string
+ */
+ public function getFormat() {
+ return $this->format;
+ }
+
+ /**
* Factory function that is just wrapper for the real constructor. It is
- * intented to be used instead of the real constructor, because it allows
+ * intended to be used instead of the real constructor, because it allows
* chaining method calls, while new objects don't.
* @since 1.17
- * @param $key String: message key
+ * @param string $key message key
* @param Varargs: parameters as Strings
* @return Message: $this
*/
@@ -247,9 +291,9 @@ class Message {
public static function newFallbackSequence( /*...*/ ) {
$keys = func_get_args();
if ( func_num_args() == 1 ) {
- if ( is_array($keys[0]) ) {
+ if ( is_array( $keys[0] ) ) {
// Allow an array to be passed as the first argument instead
- $keys = array_values($keys[0]);
+ $keys = array_values( $keys[0] );
} else {
// Optimize a single string to not need special fallback handling
$keys = $keys[0];
@@ -288,7 +332,7 @@ class Message {
if ( isset( $params[0] ) && is_array( $params[0] ) ) {
$params = $params[0];
}
- foreach( $params as $param ) {
+ foreach ( $params as $param ) {
$this->parameters[] = self::rawParam( $param );
}
return $this;
@@ -306,13 +350,103 @@ class Message {
if ( isset( $params[0] ) && is_array( $params[0] ) ) {
$params = $params[0];
}
- foreach( $params as $param ) {
+ foreach ( $params as $param ) {
$this->parameters[] = self::numParam( $param );
}
return $this;
}
/**
+ * Add parameters that are durations of time and will be passed through
+ * Language::formatDuration before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function durationParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::durationParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are expiration times and will be passed through
+ * Language::formatExpiry before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function expiryParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::expiryParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are time periods and will be passed through
+ * Language::formatTimePeriod before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function timeperiodParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::timeperiodParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are file sizes and will be passed through
+ * Language::formatSize before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function sizeParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::sizeParam( $param );
+ }
+ return $this;
+ }
+
+ /**
+ * Add parameters that are bitrates and will be passed through
+ * Language::formatBitrate before substitution
+ * @since 1.22
+ * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+ * @return Message: $this
+ */
+ public function bitrateParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::bitrateParam( $param );
+ }
+ return $this;
+ }
+
+ /**
* Set the language and the title from a context object
* @since 1.19
* @param $context IContextSource
@@ -332,13 +466,14 @@ class Message {
* turned off.
* @since 1.17
* @param $lang Mixed: language code or Language object.
+ * @throws MWException
* @return Message: $this
*/
public function inLanguage( $lang ) {
if ( $lang instanceof Language || $lang instanceof StubUserLang ) {
$this->language = $lang;
} elseif ( is_string( $lang ) ) {
- if( $this->language->getCode() != $lang ) {
+ if ( $this->language->getCode() != $lang ) {
$this->language = Language::factory( $lang );
}
} else {
@@ -378,7 +513,7 @@ class Message {
* @since 1.20
*/
public function setInterfaceMessageFlag( $value ) {
- $this->interface = (bool) $value;
+ $this->interface = (bool)$value;
return $this;
}
@@ -389,7 +524,7 @@ class Message {
* @return Message: $this
*/
public function useDatabase( $value ) {
- $this->useDatabase = (bool) $value;
+ $this->useDatabase = (bool)$value;
return $this;
}
@@ -405,6 +540,18 @@ class Message {
}
/**
+ * Returns the message as a Content object.
+ * @return Content
+ */
+ public function content() {
+ if ( !$this->content ) {
+ $this->content = new MessageContent( $this );
+ }
+
+ return $this->content;
+ }
+
+ /**
* Returns the message parsed from wikitext to HTML.
* @since 1.17
* @return String: HTML
@@ -413,28 +560,37 @@ class Message {
$string = $this->fetchMessage();
if ( $string === false ) {
- $key = htmlspecialchars( is_array( $this->key ) ? $this->key[0] : $this->key );
+ $key = htmlspecialchars( is_array( $this->key ) ? $this->key[0] : $this->key );
if ( $this->format === 'plain' ) {
return '<' . $key . '>';
}
return '&lt;' . $key . '&gt;';
}
+ # Replace $* with a list of parameters for &uselang=qqx.
+ if ( strpos( $string, '$*' ) !== false ) {
+ $paramlist = '';
+ if ( $this->parameters !== array() ) {
+ $paramlist = ': $' . implode( ', $', range( 1, count( $this->parameters ) ) );
+ }
+ $string = str_replace( '$*', $paramlist, $string );
+ }
+
# Replace parameters before text parsing
$string = $this->replaceParameters( $string, 'before' );
# Maybe transform using the full parser
- if( $this->format === 'parse' ) {
+ if ( $this->format === 'parse' ) {
$string = $this->parseText( $string );
$m = array();
- if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
+ if ( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
$string = $m[1];
}
- } elseif( $this->format === 'block-parse' ){
+ } elseif ( $this->format === 'block-parse' ) {
$string = $this->parseText( $string );
- } elseif( $this->format === 'text' ){
+ } elseif ( $this->format === 'text' ) {
$string = $this->transformText( $string );
- } elseif( $this->format === 'escaped' ){
+ } elseif ( $this->format === 'escaped' ) {
$string = $this->transformText( $string );
$string = htmlspecialchars( $string, ENT_QUOTES, 'UTF-8', false );
}
@@ -447,13 +603,30 @@ class Message {
/**
* Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg:
- * $foo = Message::get($key);
+ * $foo = Message::get( $key );
* $string = "<abbr>$foo</abbr>";
* @since 1.18
* @return String
*/
public function __toString() {
- return $this->toString();
+ // PHP doesn't allow __toString to throw exceptions and will
+ // trigger a fatal error if it does. So, catch any exceptions.
+
+ try {
+ return $this->toString();
+ } catch ( Exception $ex ) {
+ try {
+ trigger_error( "Exception caught in " . __METHOD__ . " (message " . $this->key . "): "
+ . $ex, E_USER_WARNING );
+ } catch ( Exception $ex ) {
+ // Doh! Cause a fatal error after all?
+ }
+
+ if ( $this->format === 'plain' ) {
+ return '<' . $this->key . '>';
+ }
+ return '&lt;' . $this->key . '&gt;';
+ }
}
/**
@@ -477,7 +650,7 @@ class Message {
}
/**
- * Returns the message text as-is, only parameters are subsituted.
+ * Returns the message text as-is, only parameters are substituted.
* @since 1.17
* @return String: Unescaped untransformed message text.
*/
@@ -530,7 +703,7 @@ class Message {
/**
* Check whether a message does not exist, is an empty string, or is "-"
* @since 1.18
- * @return Bool: true if is is and false if not
+ * @return Bool: true if it is and false if not
*/
public function isDisabled() {
$message = $this->fetchMessage();
@@ -556,18 +729,63 @@ class Message {
}
/**
- * Substitutes any paramaters into the message text.
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function durationParam( $value ) {
+ return array( 'duration' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function expiryParam( $value ) {
+ return array( 'expiry' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function timeperiodParam( $value ) {
+ return array( 'period' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function sizeParam( $value ) {
+ return array( 'size' => $value );
+ }
+
+ /**
+ * @since 1.22
+ * @param $value
+ * @return array
+ */
+ public static function bitrateParam( $value ) {
+ return array( 'bitrate' => $value );
+ }
+
+ /**
+ * Substitutes any parameters into the message text.
* @since 1.17
- * @param $message String: the message text
- * @param $type String: either before or after
+ * @param string $message the message text
+ * @param string $type either before or after
* @return String
*/
protected function replaceParameters( $message, $type = 'before' ) {
$replacementKeys = array();
- foreach( $this->parameters as $n => $param ) {
+ foreach ( $this->parameters as $n => $param ) {
list( $paramType, $value ) = $this->extractParam( $param );
if ( $type === $paramType ) {
- $replacementKeys['$' . ($n + 1)] = $value;
+ $replacementKeys['$' . ( $n + 1 )] = $value;
}
}
$message = strtr( $message, $replacementKeys );
@@ -577,41 +795,59 @@ class Message {
/**
* Extracts the parameter type and preprocessed the value if needed.
* @since 1.18
- * @param $param String|Array: Parameter as defined in this class.
+ * @param string|array $param Parameter as defined in this class.
* @return Tuple(type, value)
*/
protected function extractParam( $param ) {
- if ( is_array( $param ) && isset( $param['raw'] ) ) {
- return array( 'after', $param['raw'] );
- } elseif ( is_array( $param ) && isset( $param['num'] ) ) {
- // Replace number params always in before step for now.
- // No support for combined raw and num params
- return array( 'before', $this->language->formatNum( $param['num'] ) );
- } elseif ( !is_array( $param ) ) {
- return array( 'before', $param );
+ if ( is_array( $param ) ) {
+ if ( isset( $param['raw'] ) ) {
+ return array( 'after', $param['raw'] );
+ } elseif ( isset( $param['num'] ) ) {
+ // Replace number params always in before step for now.
+ // No support for combined raw and num params
+ return array( 'before', $this->language->formatNum( $param['num'] ) );
+ } elseif ( isset( $param['duration'] ) ) {
+ return array( 'before', $this->language->formatDuration( $param['duration'] ) );
+ } elseif ( isset( $param['expiry'] ) ) {
+ return array( 'before', $this->language->formatExpiry( $param['expiry'] ) );
+ } elseif ( isset( $param['period'] ) ) {
+ return array( 'before', $this->language->formatTimePeriod( $param['period'] ) );
+ } elseif ( isset( $param['size'] ) ) {
+ return array( 'before', $this->language->formatSize( $param['size'] ) );
+ } elseif ( isset( $param['bitrate'] ) ) {
+ return array( 'before', $this->language->formatBitrate( $param['bitrate'] ) );
+ } else {
+ trigger_error(
+ "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
+ E_USER_WARNING
+ );
+ return array( 'before', '[INVALID]' );
+ }
+ } elseif ( $param instanceof Message ) {
+ // Message objects should not be before parameters because
+ // then they'll get double escaped. If the message needs to be
+ // escaped, it'll happen right here when we call toString().
+ return array( 'after', $param->toString() );
} else {
- trigger_error(
- "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
- E_USER_WARNING
- );
- return array( 'before', '[INVALID]' );
+ return array( 'before', $param );
}
}
/**
* Wrapper for what ever method we use to parse wikitext.
* @since 1.17
- * @param $string String: Wikitext message contents
+ * @param string $string Wikitext message contents
* @return string Wikitext parsed into HTML
*/
protected function parseText( $string ) {
- return MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language )->getText();
+ $out = MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language );
+ return is_object( $out ) ? $out->getText() : $out;
}
/**
* Wrapper for what ever method we use to {{-transform wikitext.
* @since 1.17
- * @param $string String: Wikitext message contents
+ * @param string $string Wikitext message contents
* @return string Wikitext with {{-constructs replaced with their values.
*/
protected function transformText( $string ) {
@@ -621,6 +857,7 @@ class Message {
/**
* Wrapper for what ever method we use to get message contents
* @since 1.17
+ * @throws MWException
* @return string
*/
protected function fetchMessage() {
@@ -645,3 +882,45 @@ class Message {
}
}
+
+/**
+ * Variant of the Message class.
+ *
+ * Rather than treating the message key as a lookup
+ * value (which is passed to the MessageCache and
+ * translated as necessary), a RawMessage key is
+ * treated as the actual message.
+ *
+ * All other functionality (parsing, escaping, etc.)
+ * is preserved.
+ *
+ * @since 1.21
+ */
+class RawMessage extends Message {
+ /**
+ * Call the parent constructor, then store the key as
+ * the message.
+ *
+ * @param string $key Message to use
+ * @param array $params Parameters for the message
+ * @see Message::__construct
+ */
+ public function __construct( $key, $params = array() ) {
+ parent::__construct( $key, $params );
+ // The key is the message.
+ $this->message = $key;
+ }
+
+ /**
+ * Fetch the message (in this case, the key).
+ *
+ * @return string
+ */
+ public function fetchMessage() {
+ // Just in case the message is unset somewhere.
+ if ( !isset( $this->message ) ) {
+ $this->message = $this->key;
+ }
+ return $this->message;
+ }
+}