Completed
Push — master ( 17da57...0d4fd4 )
by Denis
02:07
created

ResponseParser::decodeResult()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
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\ServerErrorException;
12
use PhpJsonRpc\Error\ParseErrorException;
13
use PhpJsonRpc\Error\InvalidResponseException;
14
15
class ResponseParser
16
{
17
    /**
18
     * @var Interceptor
19
     */
20
    private $preParse;
21
22
    /**
23
     * ResponseParser constructor.
24
     */
25
    public function __construct()
26
    {
27
        $this->preParse = Interceptor::createBase();
28
    }
29
30
    /**
31
     * @param string $payload
32
     *
33
     * @return ResultSpec
34
     */
35
    public function parse(string $payload): ResultSpec
36
    {
37
        $data = @json_decode($payload, true);
38
39
        if (!is_array($data)) {
40
            throw new ParseErrorException();
41
        }
42
43
        $units = [];
44
45
        if ($this->isSingleResponse($data)) {
46
            $units[] = $this->decodeResult($data);
47
            return new ResultSpec($units, true);
48
        }
49
50
        /** @var array $data */
51
        foreach ($data as $response) {
52
            $units[] = $this->decodeResult($response);
53
        }
54
55
        return new ResultSpec($units, false);
56
    }
57
58
    /**
59
     * @return Interceptor
60
     */
61
    public function onPreParse(): Interceptor
62
    {
63
        return $this->preParse;
64
    }
65
66
    /**
67
     * @param array $record
68
     *
69
     * @return AbstractResult
70
     */
71
    private function decodeResult(array $record): AbstractResult
72
    {
73
        $record = $this->preParse($record);
74
75
        if ($this->isValidResult($record)) {
76
            $unit = new Result($record['id'], $record['result']);
77
        } elseif ($this->isValidError($record)) {
78
            $unit = new Error($record['id'], new ServerErrorException($record['error']['message'], $record['error']['code']));
79
        } else {
80
            throw new InvalidResponseException();
81
        }
82
83
        return $unit;
84
    }
85
86
    /**
87
     * @param array $response
88
     *
89
     * @return bool
90
     */
91
    private function isSingleResponse(array $response): bool
92
    {
93
        return array_keys($response) !== range(0, count($response) - 1);
94
    }
95
96
    /**
97
     * @param array $payload
98
     *
99
     * @return bool
100
     */
101
    private function isValidResult($payload): bool
102
    {
103
        if (!is_array($payload)) {
104
            return false;
105
        }
106
107
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
108
        $resultValid = array_key_exists('result', $payload);
109
        $idValid     = array_key_exists('id', $payload);
110
111
        return $headerValid && $resultValid && $idValid;
112
    }
113
114
    /**
115
     * @param array $payload
116
     *
117
     * @return bool
118
     */
119
    private function isValidError($payload): bool
120
    {
121
        if (!is_array($payload)) {
122
            return false;
123
        }
124
125
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
126
        $errorValid  = array_key_exists('error', $payload) && is_array($payload['error'])
127
                       && array_key_exists('code', $payload['error']) && is_int($payload['error']['code'])
128
                       && array_key_exists('message', $payload['error']) && is_string($payload['error']['message']);
129
        $idValid     = array_key_exists('id', $payload);
130
131
        return $headerValid && $errorValid && $idValid;
132
    }
133
134
    /**
135
     * @param array $data
136
     *
137
     * @return array
138
     */
139
    private function preParse(array $data): array
140
    {
141
        $result = $this->preParse->handle(new ParserContainer($this, $data));
142
143
        if ($result instanceof ParserContainer) {
144
            return $result->getValue();
145
        }
146
147
        throw new \RuntimeException();
148
    }
149
}
150