Allow injecting a message localizer into Status
The coupling of Status with the global request context for Message object creation is a common pain point in unit tests and in no-session code. As a short term solution (until Status is properly deprecated) allow injecting a different localizer. Also refactor the code a bit to get rid if the explicit need for the context language (which is already implicit in the localizer). Change-Id: I82a2e4a83743546a934fb938b94e877a2471a3d2
This commit is contained in:
parent
8074fc63e8
commit
dd01c6dd64
3 changed files with 97 additions and 42 deletions
|
|
@ -20,8 +20,6 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
use MediaWiki\MediaWikiServices;
|
||||
|
||||
/**
|
||||
* Generic operation result class
|
||||
* Has warning/error list, boolean status and arbitrary value
|
||||
|
|
@ -43,6 +41,9 @@ class Status extends StatusValue {
|
|||
/** @var callable|false */
|
||||
public $cleanCallback = false;
|
||||
|
||||
/** @var MessageLocalizer|null */
|
||||
protected $messageLocalizer;
|
||||
|
||||
/**
|
||||
* Succinct helper method to wrap a StatusValue
|
||||
*
|
||||
|
|
@ -77,7 +78,7 @@ class Status extends StatusValue {
|
|||
* @return mixed
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
function __get( $name ) {
|
||||
public function __get( $name ) {
|
||||
if ( $name === 'ok' ) {
|
||||
return $this->isOK();
|
||||
}
|
||||
|
|
@ -96,7 +97,7 @@ class Status extends StatusValue {
|
|||
* @param mixed $value
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
function __set( $name, $value ) {
|
||||
public function __set( $name, $value ) {
|
||||
if ( $name === 'ok' ) {
|
||||
$this->setOK( $value );
|
||||
} elseif ( !property_exists( $this, $name ) ) {
|
||||
|
|
@ -107,6 +108,20 @@ class Status extends StatusValue {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes this Status object use the given localizer instead of the global one.
|
||||
* If it is an IContextSource or a ResourceLoaderContext, it will also be used to
|
||||
* determine the interface language.
|
||||
* @note This setting does not survive serialization. That's usually for the best
|
||||
* (there's no guarantee we'll still have the same localization settings after
|
||||
* unserialization); it is the caller's responsibility to set the localizer again
|
||||
* if needed.
|
||||
* @param MessageLocalizer $messageLocalizer
|
||||
*/
|
||||
public function setMessageLocalizer( MessageLocalizer $messageLocalizer ) {
|
||||
$this->messageLocalizer = $messageLocalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits this Status object into two new Status objects, one which contains only
|
||||
* the error messages, and one that contains the warnings, only. The returned array is
|
||||
|
|
@ -117,10 +132,17 @@ class Status extends StatusValue {
|
|||
* ]
|
||||
*
|
||||
* @return Status[]
|
||||
* @suppress PhanUndeclaredProperty Status vs StatusValue
|
||||
*/
|
||||
public function splitByErrorType() {
|
||||
list( $errorsOnlyStatus, $warningsOnlyStatus ) = parent::splitByErrorType();
|
||||
// phan/phan#2133?
|
||||
'@phan-var Status $errorsOnlyStatus';
|
||||
'@phan-var Status $warningsOnlyStatus';
|
||||
|
||||
if ( $this->messageLocalizer ) {
|
||||
$errorsOnlyStatus->setMessageLocalizer( $this->messageLocalizer );
|
||||
$warningsOnlyStatus->setMessageLocalizer( $this->messageLocalizer );
|
||||
}
|
||||
$errorsOnlyStatus->cleanCallback =
|
||||
$warningsOnlyStatus->cleanCallback = $this->cleanCallback;
|
||||
|
||||
|
|
@ -151,33 +173,16 @@ class Status extends StatusValue {
|
|||
return $cleanParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|Language|null|StubUserLang $lang Language to use for processing
|
||||
* messages, or null to default to the user language.
|
||||
* @return Language|StubUserLang
|
||||
*/
|
||||
protected function languageFromParam( $lang ) {
|
||||
if ( $lang === null ) {
|
||||
return RequestContext::getMain()->getLanguage();
|
||||
}
|
||||
if ( $lang instanceof Language || $lang instanceof StubUserLang ) {
|
||||
return $lang;
|
||||
}
|
||||
return MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error list as a wikitext formatted list
|
||||
*
|
||||
* @param string|bool $shortContext A short enclosing context message name, to
|
||||
* be used when there is a single error
|
||||
* @param string|bool $longContext A long enclosing context message name, for a list
|
||||
* @param string|Language|null|StubUserLang $lang Language to use for processing messages
|
||||
* @param string|Language|StubUserLang|null $lang Language to use for processing messages
|
||||
* @return string
|
||||
*/
|
||||
public function getWikiText( $shortContext = false, $longContext = false, $lang = null ) {
|
||||
$lang = $this->languageFromParam( $lang );
|
||||
|
||||
$rawErrors = $this->getErrors();
|
||||
if ( count( $rawErrors ) === 0 ) {
|
||||
if ( $this->isOK() ) {
|
||||
|
|
@ -192,9 +197,9 @@ class Status extends StatusValue {
|
|||
if ( count( $rawErrors ) === 1 ) {
|
||||
$s = $this->getErrorMessage( $rawErrors[0], $lang )->plain();
|
||||
if ( $shortContext ) {
|
||||
$s = wfMessage( $shortContext, $s )->inLanguage( $lang )->plain();
|
||||
$s = $this->msgInLang( $shortContext, $lang, $s )->plain();
|
||||
} elseif ( $longContext ) {
|
||||
$s = wfMessage( $longContext, "* $s\n" )->inLanguage( $lang )->plain();
|
||||
$s = $this->msgInLang( $longContext, $lang, "* $s\n" )->plain();
|
||||
}
|
||||
} else {
|
||||
$errors = $this->getErrorMessageArray( $rawErrors, $lang );
|
||||
|
|
@ -203,9 +208,9 @@ class Status extends StatusValue {
|
|||
}
|
||||
$s = '* ' . implode( "\n* ", $errors ) . "\n";
|
||||
if ( $longContext ) {
|
||||
$s = wfMessage( $longContext, $s )->inLanguage( $lang )->plain();
|
||||
$s = $this->msgInLang( $longContext, $lang, $s )->plain();
|
||||
} elseif ( $shortContext ) {
|
||||
$s = wfMessage( $shortContext, "\n$s\n" )->inLanguage( $lang )->plain();
|
||||
$s = $this->msgInLang( $shortContext, $lang, "\n$s\n" )->plain();
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
|
|
@ -228,12 +233,10 @@ class Status extends StatusValue {
|
|||
*
|
||||
* @param string|string[]|bool $shortContext A message name or an array of message names.
|
||||
* @param string|string[]|bool $longContext A message name or an array of message names.
|
||||
* @param string|Language|null $lang Language to use for processing messages
|
||||
* @param string|Language|StubUserLang|null $lang Language to use for processing messages
|
||||
* @return Message
|
||||
*/
|
||||
public function getMessage( $shortContext = false, $longContext = false, $lang = null ) {
|
||||
$lang = $this->languageFromParam( $lang );
|
||||
|
||||
$rawErrors = $this->getErrors();
|
||||
if ( count( $rawErrors ) === 0 ) {
|
||||
if ( $this->isOK() ) {
|
||||
|
|
@ -248,11 +251,11 @@ class Status extends StatusValue {
|
|||
if ( count( $rawErrors ) === 1 ) {
|
||||
$s = $this->getErrorMessage( $rawErrors[0], $lang );
|
||||
if ( $shortContext ) {
|
||||
$s = wfMessage( $shortContext, $s )->inLanguage( $lang );
|
||||
$s = $this->msgInLang( $shortContext, $lang, $s );
|
||||
} elseif ( $longContext ) {
|
||||
$wrapper = new RawMessage( "* \$1\n" );
|
||||
$wrapper->params( $s )->parse();
|
||||
$s = wfMessage( $longContext, $wrapper )->inLanguage( $lang );
|
||||
$s = $this->msgInLang( $longContext, $lang, $wrapper );
|
||||
}
|
||||
} else {
|
||||
$msgs = $this->getErrorMessageArray( $rawErrors, $lang );
|
||||
|
|
@ -262,11 +265,11 @@ class Status extends StatusValue {
|
|||
$s->params( $msgs )->parse();
|
||||
|
||||
if ( $longContext ) {
|
||||
$s = wfMessage( $longContext, $s )->inLanguage( $lang );
|
||||
$s = $this->msgInLang( $longContext, $lang, $s );
|
||||
} elseif ( $shortContext ) {
|
||||
$wrapper = new RawMessage( "\n\$1\n", [ $s ] );
|
||||
$wrapper->parse();
|
||||
$s = wfMessage( $shortContext, $wrapper )->inLanguage( $lang );
|
||||
$s = $this->msgInLang( $shortContext, $lang, $wrapper );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,20 +291,22 @@ class Status extends StatusValue {
|
|||
if ( isset( $error['message'] ) && $error['message'] instanceof Message ) {
|
||||
$msg = $error['message'];
|
||||
} elseif ( isset( $error['message'] ) && isset( $error['params'] ) ) {
|
||||
$msg = wfMessage( $error['message'],
|
||||
$msg = $this->msg( $error['message'],
|
||||
array_map( 'wfEscapeWikiText', $this->cleanParams( $error['params'] ) ) );
|
||||
} else {
|
||||
$msgName = array_shift( $error );
|
||||
$msg = wfMessage( $msgName,
|
||||
$msg = $this->msg( $msgName,
|
||||
array_map( 'wfEscapeWikiText', $this->cleanParams( $error ) ) );
|
||||
}
|
||||
} elseif ( is_string( $error ) ) {
|
||||
$msg = wfMessage( $error );
|
||||
$msg = $this->msg( $error );
|
||||
} else {
|
||||
throw new UnexpectedValueException( 'Got ' . get_class( $error ) . ' for key.' );
|
||||
}
|
||||
|
||||
$msg->inLanguage( $this->languageFromParam( $lang ) );
|
||||
if ( $lang ) {
|
||||
$msg->inLanguage( $lang );
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
|
|
@ -314,7 +319,6 @@ class Status extends StatusValue {
|
|||
* @return string
|
||||
*/
|
||||
public function getHTML( $shortContext = false, $longContext = false, $lang = null ) {
|
||||
$lang = $this->languageFromParam( $lang );
|
||||
$text = $this->getWikiText( $shortContext, $longContext, $lang );
|
||||
$out = MessageCache::singleton()->parse( $text, null, true, true, $lang );
|
||||
return $out instanceof ParserOutput
|
||||
|
|
@ -329,7 +333,6 @@ class Status extends StatusValue {
|
|||
* @return Message[]
|
||||
*/
|
||||
protected function getErrorMessageArray( $errors, $lang = null ) {
|
||||
$lang = $this->languageFromParam( $lang );
|
||||
return array_map( function ( $e ) use ( $lang ) {
|
||||
return $this->getErrorMessage( $e, $lang );
|
||||
}, $errors );
|
||||
|
|
@ -389,11 +392,13 @@ class Status extends StatusValue {
|
|||
/**
|
||||
* Don't save the callback when serializing, because Closures can't be
|
||||
* serialized and we're going to clear it in __wakeup anyway.
|
||||
* Don't save the localizer, because it can be pretty much anything. Restoring it is
|
||||
* the caller's responsibility (otherwise it will just fall back to the global request context).
|
||||
* @return array
|
||||
*/
|
||||
function __sleep() {
|
||||
$keys = array_keys( get_object_vars( $this ) );
|
||||
return array_diff( $keys, [ 'cleanCallback' ] );
|
||||
return array_diff( $keys, [ 'cleanCallback', 'messageLocalizer' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -401,5 +406,33 @@ class Status extends StatusValue {
|
|||
*/
|
||||
function __wakeup() {
|
||||
$this->cleanCallback = false;
|
||||
$this->messageLocalizer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|MessageSpecifier $key
|
||||
* @param string|string[] ...$params
|
||||
* @return Message
|
||||
*/
|
||||
private function msg( $key, ...$params ) {
|
||||
if ( $this->messageLocalizer ) {
|
||||
return $this->messageLocalizer->msg( $key, ...$params );
|
||||
} else {
|
||||
return wfMessage( $key, ...$params );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|MessageSpecifier $key
|
||||
* @param string|Language|StubUserLang|null $lang
|
||||
* @param string|string[] ...$params
|
||||
* @return Message
|
||||
*/
|
||||
private function msgInLang( $key, $lang, ...$params ) {
|
||||
$msg = $this->msg( $key, ...$params );
|
||||
if ( $lang ) {
|
||||
$msg->inLanguage( $lang );
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ class Message implements MessageSpecifier, Serializable {
|
|||
* turned off.
|
||||
*
|
||||
* @since 1.17
|
||||
* @param Language|string $lang Language code or Language object.
|
||||
* @param Language|StubUserLang|string $lang Language code or Language object.
|
||||
* @return Message $this
|
||||
* @throws MWException
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -739,4 +739,26 @@ class StatusTest extends MediaWikiLangTestCase {
|
|||
$this->assertTrue( $sw->isOK() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Status::setMessageLocalizer
|
||||
*/
|
||||
public function testSetContext() {
|
||||
$status = Status::newFatal( 'foo' );
|
||||
$status->fatal( 'bar' );
|
||||
|
||||
$messageLocalizer = $this->getMockBuilder( MessageLocalizer::class )
|
||||
->setMethods( [ 'msg' ] )
|
||||
->getMockForAbstractClass();
|
||||
$messageLocalizer->expects( $this->atLeastOnce() )
|
||||
->method( 'msg' )
|
||||
->willReturnCallback( function ( $key ) {
|
||||
return new RawMessage( $key );
|
||||
} );
|
||||
/** @var MessageLocalizer $messageLocalizer */
|
||||
$status->setMessageLocalizer( $messageLocalizer );
|
||||
$status->getWikiText();
|
||||
$status->getWikiText( null, null, 'en' );
|
||||
$status->getWikiText( 'wrap-short', 'wrap-long' );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue