diff --git a/lib/class-wp-json-server.php b/lib/class-wp-json-server.php index ce9748b4e2..1982368d32 100644 --- a/lib/class-wp-json-server.php +++ b/lib/class-wp-json-server.php @@ -285,6 +285,13 @@ public function serve_request( $path = null ) { $result = json_encode( $this->prepare_response( $result ) ); + $json_error_message = $this->get_json_last_error(); + if ( $json_error_message ) { + $json_error_obj = new WP_Error( 'json_encode_error', $json_error_message, array( 'status' => 500 ) ); + $result = $this->error_to_response( $json_error_obj ); + $result = json_encode( $result->data[0] ); + } + if ( isset( $_GET['_jsonp'] ) ) { // Prepend '/**/' to mitigate possible JSONP Flash attacks // http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ @@ -390,6 +397,12 @@ public function dispatch() { if ( $supported & self::ACCEPT_JSON ) { $data = json_decode( $this->get_raw_data(), true ); + // test for json_decode() error + $json_error_message = $this->get_json_last_error(); + if ( $json_error_message ) { + return new WP_Error( 'json_decode_error', $json_error_message, array( 'status' => 500 ) ); + } + if ( $data !== null ) { $args = array_merge( $args, array( 'data' => $data ) ); } @@ -427,6 +440,26 @@ public function dispatch() { return new WP_Error( 'json_no_route', __( 'No route was found matching the URL and request method' ), array( 'status' => 404 ) ); } + /** + * Returns if an error occurred during most recent JSON encode/decode + * Strings to be translated will be in format like "Encoding error: Maximum stack depth exceeded" + * + * @return boolean|string Boolean false or string error message + */ + protected function get_json_last_error( ) { + // see https://core.trac.wordpress.org/ticket/27799 + if ( ! function_exists( 'json_last_error' ) ) { + return false; + } + + $last_error_code = json_last_error(); + if ( ( defined( 'JSON_ERROR_NONE' ) && $last_error_code === JSON_ERROR_NONE ) || empty( $last_error_code ) ) { + return false; + } + + return json_last_error_msg(); + } + /** * Sort parameters by order specified in method declaration * diff --git a/plugin.php b/plugin.php index d0ec540e02..2ba7dd0fdf 100644 --- a/plugin.php +++ b/plugin.php @@ -697,3 +697,56 @@ function json_handle_options_request( $response, $handler ) { return $response; } + +if ( ! function_exists( 'json_last_error_msg' ) ): +/** + * Returns the error string of the last json_encode() or json_decode() call + * + * @internal This is a compatibility function for PHP <5.5 + * + * @return boolean|string Returns the error message on success, "No Error" if no error has occurred, or FALSE on failure. + */ +function json_last_error_msg() { + // see https://core.trac.wordpress.org/ticket/27799 + if ( ! function_exists( 'json_last_error' ) ) { + return false; + } + + $last_error_code = json_last_error(); + + // just in case JSON_ERROR_NONE is not defined + $error_code_none = defined( 'JSON_ERROR_NONE' ) ? JSON_ERROR_NONE : 0; + + switch ( true ) { + case $last_error_code === $error_code_none: + return 'No error'; + + case defined( 'JSON_ERROR_DEPTH' ) && JSON_ERROR_DEPTH === $last_error_code: + return 'Maximum stack depth exceeded'; + + case defined( 'JSON_ERROR_STATE_MISMATCH' ) && JSON_ERROR_STATE_MISMATCH === $last_error_code: + return 'State mismatch (invalid or malformed JSON)'; + + case defined( 'JSON_ERROR_CTRL_CHAR' ) && JSON_ERROR_CTRL_CHAR === $last_error_code: + return 'Control character error, possibly incorrectly encoded'; + + case defined( 'JSON_ERROR_SYNTAX' ) && JSON_ERROR_SYNTAX === $last_error_code: + return 'Syntax error'; + + case defined( 'JSON_ERROR_UTF8' ) && JSON_ERROR_UTF8 === $last_error_code: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + + case defined( 'JSON_ERROR_RECURSION' ) && JSON_ERROR_RECURSION === $last_error_code: + return 'Recursion detected'; + + case defined( 'JSON_ERROR_INF_OR_NAN' ) && JSON_ERROR_INF_OR_NAN === $last_error_code: + return 'Inf and NaN cannot be JSON encoded'; + + case defined( 'JSON_ERROR_UNSUPPORTED_TYPE' ) && JSON_ERROR_UNSUPPORTED_TYPE === $last_error_code: + return 'Type is not supported'; + + default: + return 'An unknown error occurred'; + } +} +endif;