Make JSON error handling logic reusable

refs #2728
This commit is contained in:
Alexander A. Klimov 2017-02-09 19:50:04 +01:00
parent 6fbec7134c
commit 96ef0dccf8
7 changed files with 118 additions and 64 deletions

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
/**
* Exception thrown by {@link \Icinga\Util\Json::decode()} on failure
*/
class JsonDecodeException extends JsonException
{
}

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
/**
* Exception thrown by {@link \Icinga\Util\Json::encode()} on failure
*/
class JsonEncodeException extends JsonException
{
}

View File

@ -0,0 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
use Icinga\Exception\IcingaException;
/**
* Exception thrown by {@link \Icinga\Util\Json} on failure
*/
abstract class JsonException extends IcingaException
{
}

View File

@ -0,0 +1,80 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Util;
use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Exception\Json\JsonEncodeException;
/**
* Wrap {@link json_encode()} and {@link json_decode()} with error handling
*/
class Json
{
/**
* {@link json_encode()} wrapper
*
* @param mixed $value
* @param int $options
* @param int $depth
*
* @return string
* @throws JsonEncodeException
*/
public static function encode($value, $options = 0, $depth = 512)
{
$encoded = json_encode($value, $options, $depth);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonEncodeException('%s: %s', static::lastErrorMsg(), var_export($value, true));
}
return $encoded;
}
/**
* {@link json_decode()} wrapper
*
* @param string $json
* @param bool $assoc
* @param int $depth
* @param int $options
*
* @return mixed
* @throws JsonDecodeException
*/
public static function decode($json, $assoc = false, $depth = 512, $options = 0)
{
$decoded = json_decode($json, $assoc, $depth, $options);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonDecodeException('%s: %s', static::lastErrorMsg(), var_export($json, true));
}
return $decoded;
}
/**
* {@link json_last_error_msg()} replacement for PHP < 5.5.0
*
* @return string
*/
protected static function lastErrorMsg()
{
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
return json_last_error_msg();
}
// All possible error codes before PHP 5.5.0 (except JSON_ERROR_NONE)
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded';
case JSON_ERROR_STATE_MISMATCH:
return 'State mismatch (invalid or malformed JSON)';
case JSON_ERROR_CTRL_CHAR:
return 'Control character error, possibly incorrectly encoded';
case JSON_ERROR_SYNTAX:
return 'Syntax error';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return 'Unknown error';
}
}
}

View File

@ -4,11 +4,11 @@
namespace Icinga\Module\Monitoring\Command\Transport;
use Icinga\Application\Logger;
use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Module\Monitoring\Command\IcingaApiCommand;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use Icinga\Module\Monitoring\Exception\JsonDecodeException;
use Icinga\Module\Monitoring\Web\Rest\RestRequest;
/**

View File

@ -1,55 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Exception;
use Icinga\Exception\IcingaException;
/**
* Exception thrown if {@link json_decode()} fails
*/
class JsonDecodeException extends IcingaException
{
/**
* JsonDecodeException constructor
*
* @param string $invalidJson The JSON string caused this error
* @param int|null $jsonError Error code (from {@link json_last_error()}) or null for the last occurred error
*/
public function __construct($invalidJson, $jsonError = null)
{
if ($jsonError === null) {
$msg = version_compare(PHP_VERSION, '5.5.0', '>=')
? json_last_error_msg()
: $this->errorCodeToMessage(json_last_error());
} else {
$msg = $this->errorCodeToMessage($jsonError);
}
parent::__construct('%s: %s', $msg, $invalidJson);
}
/**
* Convert the given error code (from {@link json_last_error()}) to a human readable error message
*
* @param int $jsonError
* @return string
*/
protected function errorCodeToMessage($jsonError)
{
switch ($jsonError) {
case JSON_ERROR_DEPTH:
return 'The maximum stack depth has been exceeded';
case JSON_ERROR_CTRL_CHAR:
return 'Control character error, possibly incorrectly encoded';
case JSON_ERROR_STATE_MISMATCH:
return 'Invalid or malformed JSON';
case JSON_ERROR_SYNTAX:
return 'Syntax error';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return 'An error occured when parsing a JSON string';
}
}
}

View File

@ -5,7 +5,7 @@ namespace Icinga\Module\Monitoring\Web\Rest;
use Exception;
use Icinga\Application\Logger;
use Icinga\Module\Monitoring\Exception\JsonDecodeException;
use Icinga\Util\Json;
/**
* REST Request
@ -262,12 +262,6 @@ class RestRequest
fclose($stream);
}
$response = @json_decode($result, true);
if ($response === null) {
throw new JsonDecodeException($result);
}
return $response;
return Json::decode($result, true);
}
}