From: Aaron Schulz Date: Wed, 21 Jan 2015 01:23:21 +0000 (-0800) Subject: Split StatusValue out of Status class and put it in /libs X-Git-Tag: 1.31.0-rc.0~12480 X-Git-Url: http://git.cyclocoop.org/%27-%20%20.%20url_absolue%28find_in_path%28%27spip_style.css%27%29%29%20%20%20.%20url_absolue%28find_in_path%28%27prive/spip_style.css%27%29%29%20.%20%27?a=commitdiff_plain;h=c15caa6d53b47bc4205c2e3405d3193ade86a929;p=lhc%2Fweb%2Fwiklou.git Split StatusValue out of Status class and put it in /libs * Deprecated useless FileRepoStatus class Change-Id: I015635a9bf080ef6d98b2cff49b949c4378a859f --- diff --git a/autoload.php b/autoload.php index 5c515a0103..a44d279b83 100644 --- a/autoload.php +++ b/autoload.php @@ -745,6 +745,7 @@ $wgAutoloadLocalClasses = array( 'MessageBlobStore' => __DIR__ . '/includes/MessageBlobStore.php', 'MessageCache' => __DIR__ . '/includes/cache/MessageCache.php', 'MessageContent' => __DIR__ . '/includes/content/MessageContent.php', + 'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php', 'MigrateUserGroup' => __DIR__ . '/maintenance/migrateUserGroup.php', 'MimeMagic' => __DIR__ . '/includes/MimeMagic.php', 'MinifyScript' => __DIR__ . '/maintenance/minify.php', @@ -1151,6 +1152,7 @@ $wgAutoloadLocalClasses = array( 'StatCounter' => __DIR__ . '/includes/StatCounter.php', 'StatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php', 'Status' => __DIR__ . '/includes/Status.php', + 'StatusValue' => __DIR__ . '/includes/libs/StatusValue.php', 'StorageTypeStats' => __DIR__ . '/maintenance/storage/storageTypeStats.php', 'StoreFileOp' => __DIR__ . '/includes/filebackend/FileOp.php', 'StreamFile' => __DIR__ . '/includes/StreamFile.php', diff --git a/includes/Message.php b/includes/Message.php index 93a37cbb4d..49437f49ae 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -156,7 +156,7 @@ * * @since 1.17 */ -class Message { +class Message implements MessageSpecifier { /** * In which language to get this message. True, which is the default, @@ -276,7 +276,7 @@ class Message { * Returns the message key. * * If a list of multiple possible keys was supplied to the constructor, this method may - * return any of these keys. After the message ahs been fetched, this method will return + * return any of these keys. After the message has been fetched, this method will return * the key that was actually used to fetch the message. * * @since 1.21 diff --git a/includes/Status.php b/includes/Status.php index fb267bdeab..61a00470a5 100644 --- a/includes/Status.php +++ b/includes/Status.php @@ -38,41 +38,58 @@ * so that a lack of error-handling will be explicit. */ class Status { - /** @var bool */ - public $ok = true; + /** @var StatusValue */ + protected $sv; /** @var mixed */ public $value; - - /** Counters for batch operations */ - /** @var int */ + /** @var array Map of (key => bool) to indicate success of each part of batch operations */ + public $success = array(); + /** @var int Counter for batch operations */ public $successCount = 0; - - /** @var int */ + /** @var int Counter for batch operations */ public $failCount = 0; - /** Array to indicate which items of the batch operations were successful */ - /** @var array */ - public $success = array(); - - /** @var array */ - public $errors = array(); - /** @var callable */ public $cleanCallback = false; + /** + * @param StatusValue $sv [optional] + */ + public function __construct( StatusValue $sv = null ) { + $this->sv = ( $sv === null ) ? new StatusValue() : $sv; + // B/C field aliases + $this->value =& $this->sv->value; + $this->successCount =& $this->sv->successCount; + $this->failCount =& $this->sv->failCount; + $this->success =& $this->sv->success; + } + + /** + * Succinct helper method to wrap a StatusValue + * + * This is is useful when formatting StatusValue objects: + * + * $this->getOutput()->addHtml( Status::wrap( $sv )->getHTML() ); + * + * + * @param StatusValue|Status $sv + * @return Status + */ + public static function wrap( $sv ) { + return $sv instanceof Status ? $sv : new self( $sv ); + } + /** * Factory function for fatal errors * * @param string|Message $message Message name or object * @return Status */ - static function newFatal( $message /*, parameters...*/ ) { - $params = func_get_args(); - $result = new self; - call_user_func_array( array( &$result, 'error' ), $params ); - $result->ok = false; - return $result; + public static function newFatal( $message /*, parameters...*/ ) { + return new self( call_user_func_array( + array( 'StatusValue', 'newFatal' ), func_get_args() + ) ); } /** @@ -81,10 +98,11 @@ class Status { * @param mixed $value * @return Status */ - static function newGood( $value = null ) { - $result = new self; - $result->value = $value; - return $result; + public static function newGood( $value = null ) { + $sv = new StatusValue(); + $sv->value = $value; + + return new self( $sv ); } /** @@ -94,8 +112,7 @@ class Status { * @param mixed $value */ public function setResult( $ok, $value = null ) { - $this->ok = $ok; - $this->value = $value; + $this->sv->setResult( $ok, $value ); } /** @@ -105,7 +122,7 @@ class Status { * @return bool */ public function isGood() { - return $this->ok && !$this->errors; + return $this->sv->isGood(); } /** @@ -114,7 +131,7 @@ class Status { * @return bool */ public function isOK() { - return $this->ok; + return $this->sv->isOK(); } /** @@ -123,11 +140,7 @@ class Status { * @param string|Message $message Message name or object */ public function warning( $message /*, parameters... */ ) { - $params = array_slice( func_get_args(), 1 ); - $this->errors[] = array( - 'type' => 'warning', - 'message' => $message, - 'params' => $params ); + call_user_func_array( array( $this->sv, 'warning' ), func_get_args() ); } /** @@ -137,11 +150,7 @@ class Status { * @param string|Message $message Message name or object */ public function error( $message /*, parameters... */ ) { - $params = array_slice( func_get_args(), 1 ); - $this->errors[] = array( - 'type' => 'error', - 'message' => $message, - 'params' => $params ); + call_user_func_array( array( $this->sv, 'error' ), func_get_args() ); } /** @@ -151,35 +160,14 @@ class Status { * @param string|Message $message Message name or object */ public function fatal( $message /*, parameters... */ ) { - $params = array_slice( func_get_args(), 1 ); - $this->errors[] = array( - 'type' => 'error', - 'message' => $message, - 'params' => $params ); - $this->ok = false; - } - - /** - * Don't save the callback when serializing, because Closures can't be - * serialized and we're going to clear it in __wakeup anyway. - */ - public function __sleep() { - $keys = array_keys( get_object_vars( $this ) ); - return array_diff( $keys, array( 'cleanCallback' ) ); - } - - /** - * Sanitize the callback parameter on wakeup, to avoid arbitrary execution. - */ - public function __wakeup() { - $this->cleanCallback = false; + call_user_func_array( array( $this->sv, 'fatal' ), func_get_args() ); } /** * @param array $params * @return array */ - protected function cleanParams( $params ) { + protected function cleanParams( array $params ) { if ( !$this->cleanCallback ) { return $params; } @@ -199,24 +187,26 @@ class Status { * @return string */ public function getWikiText( $shortContext = false, $longContext = false ) { - if ( count( $this->errors ) == 0 ) { - if ( $this->ok ) { - $this->fatal( 'internalerror_info', + $rawErrors = $this->sv->getErrors(); + if ( count( $rawErrors ) == 0 ) { + if ( $this->sv->isOK() ) { + $this->sv->fatal( 'internalerror_info', __METHOD__ . " called for a good result, this is incorrect\n" ); } else { - $this->fatal( 'internalerror_info', + $this->sv->fatal( 'internalerror_info', __METHOD__ . ": Invalid result object: no error text but not OK\n" ); } + $rawErrors = $this->sv->getErrors(); // just added a fatal } - if ( count( $this->errors ) == 1 ) { - $s = $this->getErrorMessage( $this->errors[0] )->plain(); + if ( count( $rawErrors ) == 1 ) { + $s = $this->getErrorMessage( $rawErrors[0] )->plain(); if ( $shortContext ) { $s = wfMessage( $shortContext, $s )->plain(); } elseif ( $longContext ) { $s = wfMessage( $longContext, "* $s\n" )->plain(); } } else { - $errors = $this->getErrorMessageArray( $this->errors ); + $errors = $this->getErrorMessageArray( $rawErrors ); foreach ( $errors as &$error ) { $error = $error->plain(); } @@ -241,17 +231,19 @@ class Status { * @return Message */ public function getMessage( $shortContext = false, $longContext = false ) { - if ( count( $this->errors ) == 0 ) { - if ( $this->ok ) { - $this->fatal( 'internalerror_info', + $rawErrors = $this->sv->getErrors(); + if ( count( $rawErrors ) == 0 ) { + if ( $this->sv->isOK() ) { + $this->sv->fatal( 'internalerror_info', __METHOD__ . " called for a good result, this is incorrect\n" ); } else { - $this->fatal( 'internalerror_info', + $this->sv->fatal( 'internalerror_info', __METHOD__ . ": Invalid result object: no error text but not OK\n" ); } + $rawErrors = $this->sv->getErrors(); // just added a fatal } - if ( count( $this->errors ) == 1 ) { - $s = $this->getErrorMessage( $this->errors[0] ); + if ( count( $rawErrors ) == 1 ) { + $s = $this->getErrorMessage( $rawErrors[0] ); if ( $shortContext ) { $s = wfMessage( $shortContext, $s ); } elseif ( $longContext ) { @@ -260,7 +252,7 @@ class Status { $s = wfMessage( $longContext, $wrapper ); } } else { - $msgs = $this->getErrorMessageArray( $this->errors ); + $msgs = $this->getErrorMessageArray( $rawErrors ); $msgCount = count( $msgs ); if ( $shortContext ) { @@ -339,13 +331,7 @@ class Status { * @param bool $overwriteValue Whether to override the "value" member */ public function merge( $other, $overwriteValue = false ) { - $this->errors = array_merge( $this->errors, $other->errors ); - $this->ok = $this->ok && $other->ok; - if ( $overwriteValue ) { - $this->value = $other->value; - } - $this->successCount += $other->successCount; - $this->failCount += $other->failCount; + $this->sv->merge( $other->sv, $overwriteValue ); } /** @@ -353,9 +339,10 @@ class Status { * * @return array A list in which each entry is an array with a message key as its first element. * The remaining array elements are the message parameters. + * @deprecated 1.25 */ public function getErrorsArray() { - return $this->getStatusArray( "error" ); + return $this->getStatusArray( 'error' ); } /** @@ -363,21 +350,26 @@ class Status { * * @return array A list in which each entry is an array with a message key as its first element. * The remaining array elements are the message parameters. + * @deprecated 1.25 */ public function getWarningsArray() { - return $this->getStatusArray( "warning" ); + return $this->getStatusArray( 'warning' ); } /** * Returns a list of status messages of the given type (or all if false) + * + * @note: this handles RawMessage poorly + * * @param string $type * @return array */ protected function getStatusArray( $type = false ) { $result = array(); - foreach ( $this->errors as $error ) { + + foreach ( $this->sv->getErrors() as $error ) { if ( $type === false || $error['type'] === $type ) { - if ( $error['message'] instanceof Message ) { + if ( $error['message'] instanceof MessageSpecifier ) { $result[] = array_merge( array( $error['message']->getKey() ), $error['message']->getParams() @@ -402,13 +394,7 @@ class Status { * @return array */ public function getErrorsByType( $type ) { - $result = array(); - foreach ( $this->errors as $error ) { - if ( $error['type'] === $type ) { - $result[] = $error; - } - } - return $result; + return $this->sv->getErrorsByType( $type ); } /** @@ -419,19 +405,7 @@ class Status { * @return bool */ public function hasMessage( $message ) { - if ( $message instanceof Message ) { - $message = $message->getKey(); - } - foreach ( $this->errors as $error ) { - if ( $error['message'] instanceof Message - && $error['message']->getKey() === $message - ) { - return true; - } elseif ( $error['message'] === $message ) { - return true; - } - } - return false; + return $this->sv->hasMessage( $message ); } /** @@ -446,61 +420,67 @@ class Status { * @return bool Return true if the replacement was done, false otherwise. */ public function replaceMessage( $source, $dest ) { - $replaced = false; - foreach ( $this->errors as $index => $error ) { - if ( $error['message'] === $source ) { - $this->errors[$index]['message'] = $dest; - $replaced = true; - } - } - return $replaced; + return $this->sv->replaceMessage( $source, $dest ); } /** * @return mixed */ public function getValue() { - return $this->value; + return $this->sv->getValue(); } /** - * @return string + * Backwards compatibility logic + * + * @param string $name */ - public function __toString() { - $status = $this->isOK() ? "OK" : "Error"; - if ( count( $this->errors ) ) { - $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way"; - } else { - $errorcount = "no errors detected"; + function __get( $name ) { + if ( $name === 'ok' ) { + return $this->sv->getOK(); + } elseif ( $name === 'errors' ) { + return $this->sv->getErrors(); } - if ( isset( $this->value ) ) { - $valstr = gettype( $this->value ) . " value set"; - if ( is_object( $this->value ) ) { - $valstr .= "\"" . get_class( $this->value ) . "\" instance"; - } + throw new Exception( "Cannot get '$name' property." ); + } + + /** + * Backwards compatibility logic + * + * @param string $name + * @param mixed $value + */ + function __set( $name, $value ) { + if ( $name === 'ok' ) { + $this->sv->setOK( $value ); + } elseif ( !property_exists( $this, $name ) ) { + // Caller is using undeclared ad-hoc properties + $this->$name = $value; } else { - $valstr = "no value set"; + throw new Exception( "Cannot set '$name' property." ); } - $out = sprintf( "<%s, %s, %s>", - $status, - $errorcount, - $valstr - ); - if ( count( $this->errors ) > 0 ) { - $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" ); - $i = 1; - $out .= "\n"; - $out .= $hdr; - foreach ( $this->getStatusArray() as $stat ) { - $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n", - $i, - $stat[0], - implode( " ", array_slice( $stat, 1 ) ) - ); - $i += 1; - } - $out .= $hdr; - }; - return $out; + } + + /** + * @return string + */ + public function __toString() { + return $this->sv->__toString(); + } + + /** + * Don't save the callback when serializing, because Closures can't be + * serialized and we're going to clear it in __wakeup anyway. + */ + function __sleep() { + $keys = array_keys( get_object_vars( $this ) ); + return array_diff( $keys, array( 'cleanCallback' ) ); + } + + /** + * Sanitize the callback parameter on wakeup, to avoid arbitrary execution. + */ + function __wakeup() { + $this->cleanCallback = false; } } diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index 58245a5dd2..03934165b3 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -1681,23 +1681,26 @@ class FileRepo { * Create a new fatal error * * @param string $message - * @return FileRepoStatus + * @return Status */ public function newFatal( $message /*, parameters...*/ ) { - $params = func_get_args(); - array_unshift( $params, $this ); + $status = call_user_func_array( array( 'Status', 'newFatal' ), func_get_args() ); + $status->cleanCallback = $this->getErrorCleanupFunction(); - return call_user_func_array( array( 'FileRepoStatus', 'newFatal' ), $params ); + return $status; } /** * Create a new good result * * @param null|string $value - * @return FileRepoStatus + * @return Status */ public function newGood( $value = null ) { - return FileRepoStatus::newGood( $this, $value ); + $status = Status::newGood( $this, $value ); + $status->cleanCallback = $this->getErrorCleanupFunction(); + + return $status; } /** diff --git a/includes/filerepo/FileRepoStatus.php b/includes/filerepo/FileRepoStatus.php index 56848df286..daf26ba969 100644 --- a/includes/filerepo/FileRepoStatus.php +++ b/includes/filerepo/FileRepoStatus.php @@ -24,41 +24,6 @@ /** * Generic operation result class for FileRepo-related operations * @ingroup FileRepo + * @deprecated 1.25 */ -class FileRepoStatus extends Status { - /** - * Factory function for fatal errors - * - * @param FileRepo $repo - * @return FileRepoStatus - */ - static function newFatal( $repo /*, parameters...*/ ) { - $params = array_slice( func_get_args(), 1 ); - $result = new self( $repo ); - call_user_func_array( array( &$result, 'error' ), $params ); - $result->ok = false; - - return $result; - } - - /** - * @param FileRepo|bool $repo Default: false - * @param mixed $value - * @return FileRepoStatus - */ - static function newGood( $repo = false, $value = null ) { - $result = new self( $repo ); - $result->value = $value; - - return $result; - } - - /** - * @param bool|FileRepo $repo - */ - function __construct( $repo = false ) { - if ( $repo ) { - $this->cleanCallback = $repo->getErrorCleanupFunction(); - } - } -} +class FileRepoStatus extends Status {} diff --git a/includes/libs/MessageSpecifier.php b/includes/libs/MessageSpecifier.php new file mode 100644 index 0000000000..b417f299ae --- /dev/null +++ b/includes/libs/MessageSpecifier.php @@ -0,0 +1,39 @@ + bool) to indicate success of each part of batch operations */ + public $success = array(); + /** @var int Counter for batch operations */ + public $successCount = 0; + /** @var int Counter for batch operations */ + public $failCount = 0; + + /** + * Factory function for fatal errors + * + * @param string|MessageSpecifier $message Message key or object + * @return Status + */ + public static function newFatal( $message /*, parameters...*/ ) { + $params = func_get_args(); + $result = new static(); + call_user_func_array( array( &$result, 'fatal' ), $params ); + return $result; + } + + /** + * Factory function for good results + * + * @param mixed $value + * @return Status + */ + public static function newGood( $value = null ) { + $result = new static(); + $result->value = $value; + return $result; + } + + /** + * Returns whether the operation completed and didn't have any error or + * warnings + * + * @return bool + */ + public function isGood() { + return $this->ok && !$this->errors; + } + + /** + * Returns whether the operation completed + * + * @return bool + */ + public function isOK() { + return $this->ok; + } + + /** + * @return mixed + */ + public function getValue() { + return $this->value; + } + + /** + * Get the list of errors + * + * Each error is a (message:string or MessageSpecifier,params:array) map + * + * @return array + */ + public function getErrors() { + return $this->errors; + } + + /** + * Change operation status + * + * @param bool $ok + */ + public function setOK( $ok ) { + $this->ok = $ok; + } + + /** + * Change operation resuklt + * + * @param bool $ok Whether the operation completed + * @param mixed $value + */ + public function setResult( $ok, $value = null ) { + $this->ok = $ok; + $this->value = $value; + } + + /** + * Add a new warning + * + * @param string|MessageSpecifier $message Message key or object + */ + public function warning( $message /*, parameters... */ ) { + $this->errors[] = array( + 'type' => 'warning', + 'message' => $message, + 'params' => array_slice( func_get_args(), 1 ) + ); + } + + /** + * Add an error, do not set fatal flag + * This can be used for non-fatal errors + * + * @param string|MessageSpecifier $message Message key or object + */ + public function error( $message /*, parameters... */ ) { + $this->errors[] = array( + 'type' => 'error', + 'message' => $message, + 'params' => array_slice( func_get_args(), 1 ) + ); + } + + /** + * Add an error and set OK to false, indicating that the operation + * as a whole was fatal + * + * @param string|MessageSpecifier $message Message key or object + */ + public function fatal( $message /*, parameters... */ ) { + $this->errors[] = array( + 'type' => 'error', + 'message' => $message, + 'params' => array_slice( func_get_args(), 1 ) + ); + $this->ok = false; + } + + /** + * Merge another status object into this one + * + * @param Status $other Other Status object + * @param bool $overwriteValue Whether to override the "value" member + */ + public function merge( $other, $overwriteValue = false ) { + $this->errors = array_merge( $this->errors, $other->errors ); + $this->ok = $this->ok && $other->ok; + if ( $overwriteValue ) { + $this->value = $other->value; + } + $this->successCount += $other->successCount; + $this->failCount += $other->failCount; + } + + /** + * Returns a list of status messages of the given type + * + * Each entry is a map of (message:string or MessageSpecifier,params:array)) + * + * @param string $type + * @return array + */ + public function getErrorsByType( $type ) { + $result = array(); + foreach ( $this->errors as $error ) { + if ( $error['type'] === $type ) { + $result[] = $error; + } + } + + return $result; + } + + /** + * Returns true if the specified message is present as a warning or error + * + * @param string|MessageSpecifier $message Message key or object to search for + * + * @return bool + */ + public function hasMessage( $message ) { + if ( $message instanceof MessageSpecifier ) { + $message = $message->getKey(); + } + foreach ( $this->errors as $error ) { + if ( $error['message'] instanceof MessageSpecifier + && $error['message']->getKey() === $message + ) { + return true; + } elseif ( $error['message'] === $message ) { + return true; + } + } + + return false; + } + + /** + * If the specified source message exists, replace it with the specified + * destination message, but keep the same parameters as in the original error. + * + * Note, due to the lack of tools for comparing IStatusMessage objects, this + * function will not work when using such an object as the search parameter. + * + * @param IStatusMessage|string $source Message key or object to search for + * @param IStatusMessage|string $dest Replacement message key or object + * @return bool Return true if the replacement was done, false otherwise. + */ + public function replaceMessage( $source, $dest ) { + $replaced = false; + + foreach ( $this->errors as $index => $error ) { + if ( $error['message'] === $source ) { + $this->errors[$index]['message'] = $dest; + $replaced = true; + } + } + + return $replaced; + } + + /** + * @return string + */ + public function __toString() { + $status = $this->isOK() ? "OK" : "Error"; + if ( count( $this->errors ) ) { + $errorcount = "collected " . ( count( $this->errors ) ) . " error(s) on the way"; + } else { + $errorcount = "no errors detected"; + } + if ( isset( $this->value ) ) { + $valstr = gettype( $this->value ) . " value set"; + if ( is_object( $this->value ) ) { + $valstr .= "\"" . get_class( $this->value ) . "\" instance"; + } + } else { + $valstr = "no value set"; + } + $out = sprintf( "<%s, %s, %s>", + $status, + $errorcount, + $valstr + ); + if ( count( $this->errors ) > 0 ) { + $hdr = sprintf( "+-%'-4s-+-%'-25s-+-%'-40s-+\n", "", "", "" ); + $i = 1; + $out .= "\n"; + $out .= $hdr; + foreach ( $this->errors as $error ) { + if ( $error['message'] instanceof MessageSpecifier ) { + $key = $error['message']->getKey(); + $params = $error['message']->getParams(); + } elseif ( $error['params'] ) { + $key = $error['message']; + $params = $error['params']; + } else { + $key = $error['message']; + $params = array(); + } + + $out .= sprintf( "| %4d | %-25.25s | %-40.40s |\n", + $i, + $key, + implode( " ", $params ) + ); + $i += 1; + } + $out .= $hdr; + } + + return $out; + } +} diff --git a/tests/phpunit/includes/StatusTest.php b/tests/phpunit/includes/StatusTest.php index 628c59b617..44463cf6d7 100644 --- a/tests/phpunit/includes/StatusTest.php +++ b/tests/phpunit/includes/StatusTest.php @@ -109,7 +109,9 @@ class StatusTest extends MediaWikiLangTestCase { public function testIsGood( $ok, $errors, $expected ) { $status = new Status(); $status->ok = $ok; - $status->errors = $errors; + foreach ( $errors as $error ) { + $status->warning( $error ); + } $this->assertEquals( $expected, $status->isGood() ); }