// Ensure we parse the body data.
$body = $this->get_body();
- if ( $this->method !== 'POST' && ! empty( $body ) ) {
+
+ if ( 'POST' !== $this->method && ! empty( $body ) ) {
$this->parse_body_params();
}
$params = array();
foreach ( $order as $type ) {
- $params = array_merge( $params, (array) $this->params[ $type ] );
+ // array_merge / the "+" operator will mess up
+ // numeric keys, so instead do a manual foreach.
+ foreach ( (array) $this->params[ $type ] as $key => $value ) {
+ $params[ $key ] = $value;
+ }
}
return $params;
* Avoids parsing the JSON data until we need to access it.
*
* @since 4.4.0
+ * @since 4.7.0 Returns error instance if value cannot be decoded.
* @access protected
+ * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed.
*/
protected function parse_json_params() {
if ( $this->parsed_json ) {
- return;
+ return true;
}
$this->parsed_json = true;
$content_type = $this->get_content_type();
if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) {
- return;
+ return true;
+ }
+
+ $body = $this->get_body();
+ if ( empty( $body ) ) {
+ return true;
}
- $params = json_decode( $this->get_body(), true );
+ $params = json_decode( $body, true );
/*
* Check for a parsing error.
* might not be defined: https://core.trac.wordpress.org/ticket/27799
*/
if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) {
- return;
+ // Ensure subsequent calls receive error instance.
+ $this->parsed_json = false;
+
+ $error_data = array(
+ 'status' => WP_Http::BAD_REQUEST,
+ );
+ if ( function_exists( 'json_last_error' ) ) {
+ $error_data['json_error_code'] = json_last_error();
+ $error_data['json_error_message'] = json_last_error_msg();
+ }
+
+ return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data );
}
$this->params['JSON'] = $params;
+ return true;
}
/**
* @since 4.4.0
* @access public
*
- * @return true|null True if there are no parameters to sanitize, null otherwise.
+ * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization.
*/
public function sanitize_params() {
-
$attributes = $this->get_attributes();
// No arguments set, skip sanitizing.
$order = $this->get_parameter_order();
+ $invalid_params = array();
+
foreach ( $order as $type ) {
if ( empty( $this->params[ $type ] ) ) {
continue;
}
foreach ( $this->params[ $type ] as $key => $value ) {
- // Check if this param has a sanitize_callback added.
- if ( isset( $attributes['args'][ $key ] ) && ! empty( $attributes['args'][ $key ]['sanitize_callback'] ) ) {
- $this->params[ $type ][ $key ] = call_user_func( $attributes['args'][ $key ]['sanitize_callback'], $value, $this, $key );
+ if ( ! isset( $attributes['args'][ $key ] ) ) {
+ continue;
+ }
+ $param_args = $attributes['args'][ $key ];
+
+ // If the arg has a type but no sanitize_callback attribute, default to rest_parse_request_arg.
+ if ( ! array_key_exists( 'sanitize_callback', $param_args ) && ! empty( $param_args['type'] ) ) {
+ $param_args['sanitize_callback'] = 'rest_parse_request_arg';
+ }
+ // If there's still no sanitize_callback, nothing to do here.
+ if ( empty( $param_args['sanitize_callback'] ) ) {
+ continue;
+ }
+
+ $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();
+ } else {
+ $this->params[ $type ][ $key ] = $sanitized_value;
}
}
}
- return null;
+
+ if ( $invalid_params ) {
+ return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) );
+ }
+
+ return true;
}
/**
* WP_Error if required parameters are missing.
*/
public function has_valid_params() {
+ // If JSON data was passed, check for errors.
+ $json_error = $this->parse_json_params();
+ if ( is_wp_error( $json_error ) ) {
+ return $json_error;
+ }
$attributes = $this->get_attributes();
$required = array();
$api_root = rest_url();
if ( get_option( 'permalink_structure' ) && 0 === strpos( $url, $api_root ) ) {
- // Pretty permalinks on, and URL is under the API root
+ // Pretty permalinks on, and URL is under the API root.
$api_url_part = substr( $url, strlen( untrailingslashit( $api_root ) ) );
$route = parse_url( $api_url_part, PHP_URL_PATH );
} elseif ( ! empty( $query_params['rest_route'] ) ) {