diff options
Diffstat (limited to 'includes/Message.php')
-rw-r--r-- | includes/Message.php | 367 |
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 '<' . $key . '>'; } + # 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 '<' . $this->key . '>'; + } } /** @@ -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; + } +} |