Completed
Push — master ( f7f813...50031e )
by Denis
02:29
created

ResponseParser   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 8
dl 0
loc 153
rs 10
c 0
b 0
f 0

10 Methods

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