diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index 536efea696..6eed4e2b08 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -3182,3 +3182,57 @@ function rest_get_endpoint_args_for_schema( $schema, $method = WP_REST_Server::C return $endpoint_args; } + + +/** + * Converts an error to a response object. + * + * This iterates over all error codes and messages to change it into a flat + * array. This enables simpler client behaviour, as it is represented as a + * list in JSON rather than an object/map. + * + * @since 5.7.0 + * + * @param WP_Error $error WP_Error instance. + * + * @return WP_REST_Response List of associative arrays with code and message keys. + */ +function rest_convert_error_to_response( $error ) { + $status = array_reduce( + $error->get_all_error_data(), + function ( $status, $error_data ) { + return is_array( $error_data ) && isset( $error_data['status'] ) ? $error_data['status'] : $status; + }, + 500 + ); + + $errors = array(); + + foreach ( (array) $error->errors as $code => $messages ) { + $all_data = $error->get_all_error_data( $code ); + $last_data = array_pop( $all_data ); + + foreach ( (array) $messages as $message ) { + $formatted = array( + 'code' => $code, + 'message' => $message, + 'data' => $last_data, + ); + + if ( $all_data ) { + $formatted['additional_data'] = $all_data; + } + + $errors[] = $formatted; + } + } + + $data = $errors[0]; + if ( count( $errors ) > 1 ) { + // Remove the primary error. + array_shift( $errors ); + $data['additional_errors'] = $errors; + } + + return new WP_REST_Response( $data, $status ); +} diff --git a/wp-includes/rest-api/class-wp-rest-request.php b/wp-includes/rest-api/class-wp-rest-request.php index e972c5fa25..267ce4e01d 100644 --- a/wp-includes/rest-api/class-wp-rest-request.php +++ b/wp-includes/rest-api/class-wp-rest-request.php @@ -802,7 +802,8 @@ class WP_REST_Request implements ArrayAccess { $order = $this->get_parameter_order(); - $invalid_params = array(); + $invalid_params = array(); + $invalid_details = array(); foreach ( $order as $type ) { if ( empty( $this->params[ $type ] ) ) { @@ -825,10 +826,12 @@ class WP_REST_Request implements ArrayAccess { continue; } + /** @var mixed|WP_Error $sanitized_value */ $sanitized_value = call_user_func( $param_args['sanitize_callback'], $value, $this, $key ); if ( is_wp_error( $sanitized_value ) ) { - $invalid_params[ $key ] = $sanitized_value->get_error_message(); + $invalid_params[ $key ] = implode( ' ', $sanitized_value->get_error_messages() ); + $invalid_details[ $key ] = rest_convert_error_to_response( $sanitized_value )->get_data(); } else { $this->params[ $type ][ $key ] = $sanitized_value; } @@ -841,8 +844,9 @@ class WP_REST_Request implements ArrayAccess { /* translators: %s: List of invalid parameters. */ sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( - 'status' => 400, - 'params' => $invalid_params, + 'status' => 400, + 'params' => $invalid_params, + 'details' => $invalid_details, ) ); } @@ -894,13 +898,15 @@ class WP_REST_Request implements ArrayAccess { * * This is done after required checking as required checking is cheaper. */ - $invalid_params = array(); + $invalid_params = array(); + $invalid_details = array(); foreach ( $args as $key => $arg ) { $param = $this->get_param( $key ); if ( null !== $param && ! empty( $arg['validate_callback'] ) ) { + /** @var bool|\WP_Error $valid_check */ $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key ); if ( false === $valid_check ) { @@ -908,7 +914,8 @@ class WP_REST_Request implements ArrayAccess { } if ( is_wp_error( $valid_check ) ) { - $invalid_params[ $key ] = $valid_check->get_error_message(); + $invalid_params[ $key ] = implode( ' ', $valid_check->get_error_messages() ); + $invalid_details[ $key ] = rest_convert_error_to_response( $valid_check )->get_data(); } } } @@ -919,8 +926,9 @@ class WP_REST_Request implements ArrayAccess { /* translators: %s: List of invalid parameters. */ sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( - 'status' => 400, - 'params' => $invalid_params, + 'status' => 400, + 'params' => $invalid_params, + 'details' => $invalid_details, ) ); } diff --git a/wp-includes/rest-api/class-wp-rest-server.php b/wp-includes/rest-api/class-wp-rest-server.php index d7f8ae9a41..ed94151228 100644 --- a/wp-includes/rest-api/class-wp-rest-server.php +++ b/wp-includes/rest-api/class-wp-rest-server.php @@ -196,41 +196,13 @@ class WP_REST_Server { * list in JSON rather than an object/map. * * @since 4.4.0 + * @since 5.7.0 Converted to a wrapper of {@see rest_convert_error_to_response()}. * * @param WP_Error $error WP_Error instance. * @return WP_REST_Response List of associative arrays with code and message keys. */ protected function error_to_response( $error ) { - $error_data = $error->get_error_data(); - - if ( is_array( $error_data ) && isset( $error_data['status'] ) ) { - $status = $error_data['status']; - } else { - $status = 500; - } - - $errors = array(); - - foreach ( (array) $error->errors as $code => $messages ) { - foreach ( (array) $messages as $message ) { - $errors[] = array( - 'code' => $code, - 'message' => $message, - 'data' => $error->get_error_data( $code ), - ); - } - } - - $data = $errors[0]; - if ( count( $errors ) > 1 ) { - // Remove the primary error. - array_shift( $errors ); - $data['additional_errors'] = $errors; - } - - $response = new WP_REST_Response( $data, $status ); - - return $response; + return rest_convert_error_to_response( $error ); } /** diff --git a/wp-includes/version.php b/wp-includes/version.php index af2610ec52..57090f1b66 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.7-alpha-50149'; +$wp_version = '5.7-alpha-50150'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.