diff --git a/composer.json b/composer.json index 48301f5b2..de6e7ed12 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "docs": "https://requests.ryanmccue.info/" }, "require": { - "php": ">=5.6" + "php": ">=5.6", + "ext-json": "*" }, "require-dev": { "requests/test-server": "dev-master", diff --git a/src/Response.php b/src/Response.php index 98304fe8b..ca50de2ed 100644 --- a/src/Response.php +++ b/src/Response.php @@ -127,4 +127,37 @@ public function throw_for_status($allow_redirects = true) { throw new $exception(null, $this); } } + + /** + * JSON decode the response body. + * + * The method parameters are the same as those for the PHP native `json_decode()` function. + * + * @link https://php.net/json-decode + * + * @param ?bool $associative Optional. When `true`, JSON objects will be returned as associative arrays; + * When `false`, JSON objects will be returned as objects. + * When `null`, JSON objects will be returned as associative arrays + * or objects depending on whether `JSON_OBJECT_AS_ARRAY` is set in the flags. + * Defaults to `true` (in contrast to the PHP native default of `null`). + * @param int $depth Optional. Maximum nesting depth of the structure being decoded. + * Defaults to `512`. + * @param int $options Optional. Bitmask of JSON_BIGINT_AS_STRING, JSON_INVALID_UTF8_IGNORE, + * JSON_INVALID_UTF8_SUBSTITUTE, JSON_OBJECT_AS_ARRAY, JSON_THROW_ON_ERROR. + * Defaults to `0` (no options set). + * + * @return array + * + * @throws \WpOrg\Requests\Exception If `$this->body` is not valid json. + */ + public function decode_body($associative = true, $depth = 512, $options = 0) { + $data = json_decode($this->body, $associative, $depth, $options); + + if (json_last_error() !== JSON_ERROR_NONE) { + $last_error = json_last_error_msg(); + throw new Exception('Unable to parse JSON data: ' . $last_error, 'response.invalid', $this); + } + + return $data; + } } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php new file mode 100644 index 000000000..827d66594 --- /dev/null +++ b/tests/ResponseTest.php @@ -0,0 +1,78 @@ +expectException(Exception::class); + $this->expectExceptionMessage('Unable to parse JSON data: '); + + $response = new Response(); + $response->body = $body; + + $response->decode_body(); + } + + /** + * Data provider. + * + * @return array + */ + public function dataInvalidJsonResponse() { + $data = array( + 'text string, not JSON (syntax error)' => array('Invalid JSON'), + 'invalid JSON: single quotes (syntax error)' => array("{ 'bar': 'baz' }"), + ); + + // An empty string is only regarded as invalid JSON since PHP 7.0. + if (PHP_VERSION_ID >= 70000) { + $data['empty string (syntax error)'] = array(''); + } + + return $data; + } + + /** + * Verify correctly decoding a body in valid JSON. + * + * @requires extension json + * + * @covers ::decode_body + * + * @return void + */ + public function testJsonResponse() { + $response = new Response(); + $response->body = '{"success": false, "error": [], "data": null}'; + $decoded_body = $response->decode_body(); + + $expected = array( + 'success' => false, + 'error' => array(), + 'data' => null, + ); + + $this->assertSame($expected, $decoded_body); + } +}