ResponseParser   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 7
dl 0
loc 149
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B parse() 0 22 4
A onPreParse() 0 4 1
A decodeResult() 0 18 3
A isSingleResponse() 0 4 1
B isValidResult() 0 12 5
B isValidError() 0 14 10
A preParse() 0 10 2
1
<?php
2
3
namespace PhpJsonRpc\Client;
4
5
use PhpJsonRpc\Client\ResponseParser\ParserContainer;
6
use PhpJsonRpc\Common\Interceptor\Interceptor;
7
use PhpJsonRpc\Core\Result\AbstractResult;
8
use PhpJsonRpc\Core\Result\Error;
9
use PhpJsonRpc\Core\Result\Result;
10
use PhpJsonRpc\Core\ResultSpec;
11
use PhpJsonRpc\Error\BaseClientException;
12
use PhpJsonRpc\Error\JsonRpcException;
13
use PhpJsonRpc\Error\InvalidParamsException;
14
use PhpJsonRpc\Error\InvalidRequestException;
15
use PhpJsonRpc\Error\MethodNotFoundException;
16
use PhpJsonRpc\Error\ParseErrorException;
17
use PhpJsonRpc\Error\ServerErrorException;
18
use PhpJsonRpc\Error\InvalidResponseException;
19
20
class ResponseParser
21
{
22
    /**
23
     * @var Interceptor
24
     */
25
    private $preParse;
26
27
    /**
28
     * @var array
29
     */
30
    private $exceptionMap = [
31
        JsonRpcException::PARSE_ERROR      => ParseErrorException::class,
32
        JsonRpcException::INVALID_REQUEST  => InvalidRequestException::class,
33
        JsonRpcException::METHOD_NOT_FOUND => MethodNotFoundException::class,
34
        JsonRpcException::INVALID_PARAMS   => InvalidParamsException::class
35
    ];
36
37
    /**
38
     * ResponseParser constructor.
39
     */
40
    public function __construct()
41
    {
42
        $this->preParse = Interceptor::createBase();
43
    }
44
45
    /**
46
     * @param string $payload
47
     *
48
     * @return ResultSpec
49
     */
50
    public function parse(string $payload): ResultSpec
51
    {
52
        $data = @json_decode($payload, true);
53
54
        if (!is_array($data)) {
55
            throw new BaseClientException('Parse error', JsonRpcException::PARSE_ERROR);
56
        }
57
58
        $units = [];
59
60
        if ($this->isSingleResponse($data)) {
61
            $units[] = $this->decodeResult($data);
62
            return new ResultSpec($units, true);
63
        }
64
65
        /** @var array $data */
66
        foreach ($data as $response) {
67
            $units[] = $this->decodeResult($response);
68
        }
69
70
        return new ResultSpec($units, false);
71
    }
72
73
    /**
74
     * @return Interceptor
75
     */
76
    public function onPreParse(): Interceptor
77
    {
78
        return $this->preParse;
79
    }
80
81
    /**
82
     * @param array $record
83
     *
84
     * @return AbstractResult
85
     */
86
    private function decodeResult(array $record): AbstractResult
87
    {
88
        $record = $this->preParse($record);
89
90
        if ($this->isValidResult($record)) {
91
            $unit = new Result($record['id'], $record['result']);
92
        } elseif ($this->isValidError($record)) {
93
            $exceptionClass = $this->exceptionMap[$record['error']['code']] ?? ServerErrorException::class;
94
            $unit = new Error($record['id'], new $exceptionClass(
95
                $record['error']['data']['message'] ?? $record['error']['message'] ?? 'Server error',
96
                $record['error']['data']['code'] ?? 0
97
            ));
98
        } else {
99
            throw new InvalidResponseException();
100
        }
101
102
        return $unit;
103
    }
104
105
    /**
106
     * @param array $response
107
     *
108
     * @return bool
109
     */
110
    private function isSingleResponse(array $response): bool
111
    {
112
        return array_keys($response) !== range(0, count($response) - 1);
113
    }
114
115
    /**
116
     * @param array $payload
117
     *
118
     * @return bool
119
     */
120
    private function isValidResult($payload): bool
121
    {
122
        if (!is_array($payload)) {
123
            return false;
124
        }
125
126
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
127
        $resultValid = array_key_exists('result', $payload);
128
        $idValid     = array_key_exists('id', $payload);
129
130
        return $headerValid && $resultValid && $idValid;
131
    }
132
133
    /**
134
     * @param array $payload
135
     *
136
     * @return bool
137
     */
138
    private function isValidError($payload): bool
139
    {
140
        if (!is_array($payload)) {
141
            return false;
142
        }
143
144
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
145
        $errorValid  = array_key_exists('error', $payload) && is_array($payload['error'])
146
                       && array_key_exists('code', $payload['error']) && is_int($payload['error']['code'])
147
                       && array_key_exists('message', $payload['error']) && is_string($payload['error']['message']);
148
        $idValid     = array_key_exists('id', $payload);
149
150
        return $headerValid && $errorValid && $idValid;
151
    }
152
153
    /**
154
     * @param array $data
155
     *
156
     * @return array
157
     */
158
    private function preParse(array $data): array
159
    {
160
        $result = $this->preParse->handle(new ParserContainer($this, $data));
161
162
        if ($result instanceof ParserContainer) {
163
            return $result->getValue();
164
        }
165
166
        throw new \RuntimeException();
167
    }
168
}
169