X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/784f914b1e4b1c62d6657e86397c2e83bcee4295..4ea0dca21bda49aab5ccb91ec12bb4ef5924ed3e:/wp-includes/rest-api/class-wp-rest-request.php diff --git a/wp-includes/rest-api/class-wp-rest-request.php b/wp-includes/rest-api/class-wp-rest-request.php index 6e0720e5..4dd0dc20 100644 --- a/wp-includes/rest-api/class-wp-rest-request.php +++ b/wp-includes/rest-api/class-wp-rest-request.php @@ -20,8 +20,7 @@ * does not distinguish between arguments of the same name for different request methods. * For instance, in a request with `GET id=1` and `POST id=2`, `$request['id']` will equal * 2 (`POST`) not 1 (`GET`). For more precision between request methods, use - * {@see WP_REST_Request::get_body_params()}, {@see WP_REST_Request::get_url_params()}, - * etc. + * WP_REST_Request::get_body_params(), WP_REST_Request::get_url_params(), etc. * * @since 4.4.0 * @@ -184,7 +183,7 @@ class WP_REST_Request implements ArrayAccess { * * @link http://stackoverflow.com/q/18185366 * @link http://wiki.nginx.org/Pitfalls#Missing_.28disappearing.29_HTTP_headers - * @link http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers + * @link https://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers * * @since 4.4.0 * @access public @@ -360,7 +359,8 @@ class WP_REST_Request implements ArrayAccess { // 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(); } @@ -374,7 +374,7 @@ class WP_REST_Request implements ArrayAccess { $order[] = 'defaults'; /** - * Filter the parameter order. + * Filters the parameter order. * * The order affects which parameters are checked when using get_param() and family. * This acts similarly to PHP's `request_order` setting. @@ -451,7 +451,11 @@ class WP_REST_Request implements ArrayAccess { $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; @@ -647,11 +651,13 @@ class WP_REST_Request implements ArrayAccess { * 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; @@ -660,10 +666,15 @@ class WP_REST_Request implements ArrayAccess { $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. @@ -672,10 +683,22 @@ class WP_REST_Request implements ArrayAccess { * 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; } /** @@ -781,10 +804,9 @@ class WP_REST_Request implements ArrayAccess { * @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. @@ -794,18 +816,42 @@ class WP_REST_Request implements ArrayAccess { $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; } /** @@ -818,6 +864,11 @@ class WP_REST_Request implements ArrayAccess { * 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(); @@ -955,7 +1006,7 @@ class WP_REST_Request implements ArrayAccess { $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'] ) ) { @@ -971,7 +1022,7 @@ class WP_REST_Request implements ArrayAccess { } /** - * Filter the request generated from a URL. + * Filters the request generated from a URL. * * @since 4.5.0 *