General: Introduce the ability to merge multiple `WP_Error` objects into one another, and to store more than one item of data for an error.

This allows multiple errors to be instantiated independently but collected into one without having to manually combine their properties.

Props rmccue, dlh, TimothyBlynJacobs

Fixes #38777


git-svn-id: https://develop.svn.wordpress.org/trunk@49115 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
John Blackbourn 2020-10-09 22:20:50 +00:00
parent 797b7e1fe2
commit df7554985d
2 changed files with 204 additions and 16 deletions

View File

@ -2,8 +2,6 @@
/**
* WordPress Error API.
*
* Contains the WP_Error class and the is_wp_error() function.
*
* @package WordPress
*/
@ -27,7 +25,7 @@ class WP_Error {
public $errors = array();
/**
* Stores the list of data for error codes.
* Stores the most-recently added data for each error code.
*
* @since 2.1.0
* @var array
@ -35,7 +33,15 @@ class WP_Error {
public $error_data = array();
/**
* Initialize the error.
* Stores previously added data added for error codes, oldest-to-newest by code.
*
* @since 5.6.0
* @var array[]
*/
protected $additional_data = array();
/**
* Initializes the error.
*
* If `$code` is empty, the other parameters will be ignored.
* When `$code` is not empty, `$message` will be used even if
@ -60,7 +66,7 @@ class WP_Error {
}
/**
* Retrieve all error codes.
* Retrieves all error codes.
*
* @since 2.1.0
*
@ -75,7 +81,7 @@ class WP_Error {
}
/**
* Retrieve first error code available.
* Retrieves the first error code available.
*
* @since 2.1.0
*
@ -92,12 +98,12 @@ class WP_Error {
}
/**
* Retrieve all error messages or error messages matching code.
* Retrieves all error messages, or the error messages for the given error code.
*
* @since 2.1.0
*
* @param string|int $code Optional. Retrieve messages matching code, if exists.
* @return array Error strings on success, or empty array on failure (if using code parameter).
* @return array Error strings on success, or empty array if there are none.
*/
public function get_error_messages( $code = '' ) {
// Return all messages if no code specified.
@ -118,7 +124,7 @@ class WP_Error {
}
/**
* Get single error message.
* Gets a single error message.
*
* This will get the first message available for the code. If no code is
* given then the first code available will be used.
@ -126,7 +132,7 @@ class WP_Error {
* @since 2.1.0
*
* @param string|int $code Optional. Error code to retrieve message.
* @return string
* @return string The error message.
*/
public function get_error_message( $code = '' ) {
if ( empty( $code ) ) {
@ -140,7 +146,7 @@ class WP_Error {
}
/**
* Retrieve error data for error code.
* Retrieves the most-recently added error data for an error code.
*
* @since 2.1.0
*
@ -158,11 +164,11 @@ class WP_Error {
}
/**
* Verify if the instance contains errors.
* Verifies if the instance contains errors.
*
* @since 5.1.0
*
* @return bool
* @return bool If the instance contains errors.
*/
public function has_errors() {
if ( ! empty( $this->errors ) ) {
@ -201,11 +207,10 @@ class WP_Error {
}
/**
* Add data for error code.
*
* The error code can only contain one error data.
* Adds data to an error with the given code.
*
* @since 2.1.0
* @since 5.6.0 Errors can now contain more than one item of error data. {@see WP_Error::$additional_data}.
*
* @param mixed $data Error data.
* @param string|int $code Error code.
@ -215,9 +220,39 @@ class WP_Error {
$code = $this->get_error_code();
}
if ( isset( $this->error_data[ $code ] ) ) {
$this->additional_data[ $code ][] = $this->error_data[ $code ];
}
$this->error_data[ $code ] = $data;
}
/**
* Retrieves all error data for an error code in the order in which the data was added.
*
* @since 5.6.0
*
* @param string|int $code Error code.
* @return mixed[] Array of error data, if it exists.
*/
public function get_all_error_data( $code = '' ) {
if ( empty( $code ) ) {
$code = $this->get_error_code();
}
$data = array();
if ( isset( $this->additional_data[ $code ] ) ) {
$data = $this->additional_data[ $code ];
}
if ( isset( $this->error_data[ $code ] ) ) {
$data[] = $this->error_data[ $code ];
}
return $data;
}
/**
* Removes the specified error.
*
@ -231,5 +266,48 @@ class WP_Error {
public function remove( $code ) {
unset( $this->errors[ $code ] );
unset( $this->error_data[ $code ] );
unset( $this->additional_data[ $code ] );
}
/**
* Merges the errors in the given error object into this one.
*
* @since 5.6.0
*
* @param WP_Error $error Error object to merge.
*/
public function merge_from( WP_Error $error ) {
static::copy_errors( $error, $this );
}
/**
* Exports the errors in this object into the given one.
*
* @since 5.6.0
*
* @param WP_Error $error Error object to export into.
*/
public function export_to( WP_Error $error ) {
static::copy_errors( $this, $error );
}
/**
* Copies errors from one WP_Error instance to another.
*
* @since 5.6.0
*
* @param WP_Error $from From.
* @param WP_Error $to To.
*/
protected static function copy_errors( WP_Error $from, WP_Error $to ) {
foreach ( $from->get_error_codes() as $code ) {
foreach ( $from->get_error_messages( $code ) as $error_message ) {
$to->add( $code, $error_message );
}
foreach ( $from->get_all_error_data( $code ) as $data ) {
$to->add_data( $data, $code );
}
}
}
}

View File

@ -375,6 +375,58 @@ class Tests_WP_Error extends WP_UnitTestCase {
$this->assertSame( 'data2', $this->wp_error->get_error_data( 'code' ) );
}
/**
* @covers ::get_all_error_data
*/
public function test_get_all_error_data_with_code_and_no_errors_should_evaluate_as_empty_array() {
$this->assertSame( array(), $this->wp_error->get_all_error_data( 'code' ) );
}
/**
* @covers ::get_all_error_data
*/
public function test_get_all_error_data_with_code_and_one_error_with_no_data_should_evaluate_as_empty_array() {
$this->wp_error->add( 'code', 'message' );
$this->assertSame( array(), $this->wp_error->get_all_error_data( 'code' ) );
}
/**
* @covers ::get_all_error_data
*/
public function test_get_all_error_data_with_code_and_one_error_with_data_should_return_that_data() {
$expected = array( 'data-key' => 'data-value' );
$this->wp_error->add( 'code', 'message', $expected );
$actual = $this->wp_error->get_all_error_data( 'code' );
$this->assertCount( 1, $actual );
$this->assertSameSetsWithIndex( $expected, $actual[0] );
}
/**
* @covers ::get_all_error_data
*/
public function test_get_all_error_data_with_code_and_multiple_errors_same_code_should_return_all_data() {
$this->wp_error->add( 'code', 'message', 'data' );
$this->wp_error->add( 'code', 'message2', 'data2' );
$this->wp_error->add( 'code2', 'message3', 'data3' );
$this->assertSame( array( 'data', 'data2' ), $this->wp_error->get_all_error_data( 'code' ) );
}
/**
* @covers ::get_all_error_data
*/
public function test_get_all_error_data_should_handle_manipulation_of_error_data_property() {
$this->wp_error->add_data( 'data1', 'code' );
$this->wp_error->add_data( 'data2', 'code' );
$this->wp_error->error_data['code'] = 'dataX';
$this->assertSame( 'dataX', $this->wp_error->get_error_data( 'code' ) );
$this->assertSame( array( 'data1', 'dataX' ), $this->wp_error->get_all_error_data( 'code' ) );
}
/**
* @covers ::has_errors
*/
@ -712,10 +764,68 @@ class Tests_WP_Error extends WP_UnitTestCase {
*/
public function test_remove_should_remove_the_error_data_associated_with_the_given_code() {
$this->wp_error->add( 'code', 'message', 'data' );
$this->wp_error->add( 'code', 'message', 'data2' );
$this->wp_error->remove( 'code' );
$this->assertEmpty( $this->wp_error->error_data );
$this->assertEmpty( $this->wp_error->get_error_data( 'code' ) );
$this->assertEmpty( $this->wp_error->get_all_error_data( 'code' ) );
}
/**
* @covers ::merge_from()
*/
public function test_merge_from_should_copy_other_error_into_instance() {
$this->wp_error->add( 'code1', 'message1', 'data1' );
$other = new WP_Error( 'code1', 'message2', 'data2' );
$other->add( 'code2', 'message3' );
$this->wp_error->merge_from( $other );
$this->assertSame( array( 'message1', 'message2' ), $this->wp_error->get_error_messages( 'code1' ) );
$this->assertSame( 'data2', $this->wp_error->get_error_data( 'code1' ) );
$this->assertSame( array( 'data1', 'data2' ), $this->wp_error->get_all_error_data( 'code1' ) );
$this->assertSame( 'message3', $this->wp_error->get_error_message( 'code2' ) );
}
/**
* @covers ::merge_from()
*/
public function test_merge_from_with_no_errors_should_not_add_to_instance() {
$other = new WP_Error();
$this->wp_error->merge_from( $other );
$this->assertFalse( $this->wp_error->has_errors() );
}
/**
* @covers ::export_to()
*/
public function test_export_to_should_copy_instance_into_other_error() {
$other = new WP_Error();
$other->add( 'code1', 'message1', 'data1' );
$this->wp_error->add( 'code1', 'message2', 'data2' );
$this->wp_error->add( 'code2', 'message3' );
$this->wp_error->export_to( $other );
$this->assertSame( array( 'message1', 'message2' ), $other->get_error_messages( 'code1' ) );
$this->assertSame( 'data2', $other->get_error_data( 'code1' ) );
$this->assertSame( array( 'data1', 'data2' ), $other->get_all_error_data( 'code1' ) );
$this->assertSame( 'message3', $other->get_error_message( 'code2' ) );
}
/**
* @covers ::export_to()
*/
public function test_export_to_with_no_errors_should_not_add_to_other_error() {
$other = new WP_Error();
$this->wp_error->export_to( $other );
$this->assertFalse( $other->has_errors() );
}
}