Completed
Push — master ( 4bee86...913926 )
by Denis
04:17
created

Client::batchExecute()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 33
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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