Skip to content

Commit

Permalink
Handle more JSON errors gracefully when JSON_PARTIAL_OUTPUT_ON_ERROR …
Browse files Browse the repository at this point in the history
…is set

It's fairly common for stack traces to be circular, i.e. they can't be encoded as JSON without limiting the recursion depth somehow. PHP automatically does this if JSON_PARTIAL_OUTPUT_ON_ERROR is true, but we still considered it an error.

By treating JSON_ERROR_RECURSION as okay if JSON_PARTIAL_OUTPUT_ON_ERROR is set we prevent things from blowing up if the user uses a JSON-based exception handler.

Since exceptions thrown in exception handlers are generally a recipe for disaster I added JSON_ERROR_INF_OR_NAN to the list of "acceptable" errors too.
  • Loading branch information
Sam Stenvall committed Mar 6, 2018
1 parent 20e8419 commit 8dfef78
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
21 changes: 18 additions & 3 deletions src/Illuminate/Http/JsonResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,24 @@ public function setData($data = [])
*/
protected function hasValidJson($jsonError)
{
return $jsonError === JSON_ERROR_NONE ||
($jsonError === JSON_ERROR_UNSUPPORTED_TYPE &&
$this->hasEncodingOption(JSON_PARTIAL_OUTPUT_ON_ERROR));
// No error is obviously fine
if ($jsonError === JSON_ERROR_NONE) {
return true;
}

// If the JSON_PARTIAL_OUTPUT_ON_ERROR option is set, some additional errors are fine
// (see https://secure.php.net/manual/en/json.constants.php)
if ($this->hasEncodingOption(JSON_PARTIAL_OUTPUT_ON_ERROR)) {
$acceptableErrors = [
JSON_ERROR_RECURSION,
JSON_ERROR_INF_OR_NAN,
JSON_ERROR_UNSUPPORTED_TYPE,
];

return \in_array($jsonError, $acceptableErrors);
}

return false;
}

/**
Expand Down
23 changes: 23 additions & 0 deletions tests/Http/HttpJsonResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,29 @@ public function testJsonErrorResourceWithPartialOutputOnError()
$this->assertInstanceOf('stdClass', $data);
$this->assertNull($data->resource);
}

public function testJsonErrorRecursionDetectedWithPartialOutputOnError()
{
$objectA = new \stdClass();
$objectB = new \stdClass();
$objectA->b = $objectB;
$objectB->a = $objectA;

$response = new \Illuminate\Http\JsonResponse($objectA, 200, [], JSON_PARTIAL_OUTPUT_ON_ERROR);
$data = $response->getData();

$this->assertNotNull($data);
}

public function testJsonErrorInfOrNanWithPartialOutputOnError()
{
$data = ['product' => NAN];

$response = new \Illuminate\Http\JsonResponse($data, 200, [], JSON_PARTIAL_OUTPUT_ON_ERROR);
$data = $response->getData();

$this->assertNotNull($data);
}
}

class JsonResponseTestJsonableObject implements Jsonable
Expand Down

0 comments on commit 8dfef78

Please sign in to comment.