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

Client::getRequestBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace PhpJsonRpc;
4
5
use PhpJsonRpc\Client\IdGenerator;
6
use PhpJsonRpc\Client\IdGeneratorInterface;
7
use PhpJsonRpc\Core\Result\Error;
8
use PhpJsonRpc\Error\InvalidResponseException;
9
use PhpJsonRpc\Client\HttpTransport;
10
use PhpJsonRpc\Client\AbstractTransport;
11
use PhpJsonRpc\Client\RequestBuilder;
12
use PhpJsonRpc\Client\ResponseParser;
13
use PhpJsonRpc\Core\Invoke\AbstractInvoke;
14
use PhpJsonRpc\Core\Invoke\Notification;
15
use PhpJsonRpc\Core\Invoke\Invoke;
16
use PhpJsonRpc\Core\InvokeSpec;
17
use PhpJsonRpc\Core\Result\Result;
18
use PhpJsonRpc\Core\ResultSpec;
19
20
/**
21
 * Implementation of JSON-RPC2 client specification
22
 *
23
 * @link http://www.jsonrpc.org/specification
24
 */
25
class Client
26
{
27
    /**
28
     * @var AbstractInvoke[]
29
     */
30
    private $units = [];
31
32
    /**
33
     * @var bool
34
     */
35
    private $isSingleRequest = true;
36
37
    /**
38
     * @var RequestBuilder
39
     */
40
    private $requestBuilder;
41
42
    /**
43
     * @var AbstractTransport
44
     */
45
    private $transport;
46
47
    /**
48
     * @var ResponseParser
49
     */
50
    private $responseParser;
51
52
    /**
53
     * @var IdGeneratorInterface
54
     */
55
    private $generatorId;
56
57
    /**
58
     * Client constructor.
59
     *
60
     * @param string $url
61
     */
62
    public function __construct(string $url)
63
    {
64
        $this->requestBuilder = new RequestBuilder();
65
        $this->transport      = new HttpTransport($url);
66
        $this->responseParser = new ResponseParser();
67
        $this->generatorId    = new IdGenerator();
68
    }
69
70
    /**
71
     * @return RequestBuilder
72
     */
73
    public function getRequestBuilder(): RequestBuilder
74
    {
75
        return $this->requestBuilder;
76
    }
77
78
    /**
79
     * @return AbstractTransport
80
     */
81
    public function getTransport(): AbstractTransport
82
    {
83
        return $this->transport;
84
    }
85
86
    /**
87
     * Set transport engine
88
     *
89
     * @param AbstractTransport $transport
90
     */
91
    public function setTransport(AbstractTransport $transport)
92
    {
93
        $this->transport = $transport;
94
    }
95
96
    /**
97
     * @return ResponseParser
98
     */
99
    public function getResponseParser()
100
    {
101
        return $this->responseParser;
102
    }
103
104
    /**
105
     * @param ResponseParser $responseParser
106
     */
107
    public function setResponseParser($responseParser)
108
    {
109
        $this->responseParser = $responseParser;
110
    }
111
112
    /**
113
     * @return IdGeneratorInterface
114
     */
115
    public function getGeneratorId(): IdGeneratorInterface
116
    {
117
        return $this->generatorId;
118
    }
119
120
    /**
121
     * @param IdGeneratorInterface $idGenerator
122
     */
123
    public function setIdGenerator(IdGeneratorInterface $idGenerator)
124
    {
125
        $this->generatorId = $idGenerator;
126
    }
127
128
    /**
129
     * Make request
130
     *
131
     * @param string $method
132
     * @param array  $parameters
133
     *
134
     * @return $this|mixed
135
     */
136
    public function call(string $method, array $parameters)
137
    {
138
        $this->units[] = new Invoke($this->generatorId->get(), $method, $parameters);
139
140
        if ($this->isSingleRequest) {
141
            $resultSpecifier = $this->execute(new InvokeSpec($this->units, true));
142
143
            if (!$resultSpecifier->isSingleResult()) {
144
                throw new InvalidResponseException();
145
            }
146
147
            list($result) = $resultSpecifier->getResults();
148
149
            if ($result instanceof Result) {
150
                /** @var Result $result */
151
                return $result->getResult();
152
            }
153
154
            return null;
155
        }
156
157
        return $this;
158
    }
159
160
    /**
161
     * Make notification request
162
     *
163
     * @param string $method
164
     * @param array  $parameters
165
     *
166
     * @return $this|null
167
     */
168
    public function notification(string $method, array $parameters)
169
    {
170
        $this->units[] = new Notification($method, $parameters);
171
172
        if ($this->isSingleRequest) {
173
            $this->execute(new InvokeSpec($this->units, true));
174
            return null;
175
        }
176
177
        return $this;
178
    }
179
180
    /**
181
     * Start batch request
182
     *
183
     * @return $this
184
     */
185
    public function batch()
186
    {
187
        $this->isSingleRequest = false;
188
        return $this;
189
    }
190
191
    /**
192
     * Execute batch request
193
     *
194
     * @return array
195
     */
196
    public function batchExecute()
197
    {
198
        $results = $this->execute(new InvokeSpec($this->units, false))->getResults();
199
200
        // Make right order in sequence of results. It's required operation, because JSON-RPC2
201
        // specification define: "The Response objects being returned from a batch call MAY be returned
202
        // in any order within the Array. The Client SHOULD match contexts between the set of Request objects and the
203
        // resulting set of Response objects based on the id member within each Object."
204
205
        $callMap = [];
206
        foreach ($this->units as $index => $unit) {
207
            /** @var Invoke $unit */
208
            $callMap[$unit->getRawId()] = $index;
209
        }
210
211
        if (count($results) !== count($this->units)) {
212
            throw new InvalidResponseException();
213
        }
214
215
        $resultSequence = [];
216
        foreach ($results as $result) {
217
            if ($result instanceof Result) {
218
                /** @var Result $result */
219
                $resultSequence[ $callMap[$result->getId()] ] = $result->getResult();
220
            } elseif ($result instanceof Error) {
221
                /** @var Error $result */
222
                $resultSequence[ $callMap[$result->getId()] ] = null;
223
            }
224
        }
225
        ksort($resultSequence);
226
227
        return $resultSequence;
228
    }
229
230
    /**
231
     * @param InvokeSpec $call
232
     *
233
     * @return ResultSpec
234
     */
235
    private function execute(InvokeSpec $call): ResultSpec
236
    {
237
        $request  = $this->requestBuilder->build($call);
238
        $response = $this->transport->request($request);
239
240
        return $this->responseParser->parse($response);
241
    }
242
}
243