Passed
Pull Request — master (#14)
by Sandro
03:13
created

Statement::next()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * Sandro Keil (https://sandro-keil.de)
4
 *
5
 * @link      http://github.com/sandrokeil/arangodb-php-client for the canonical source repository
6
 * @copyright Copyright (c) 2018-2020 Sandro Keil
7
 * @license   http://github.com/sandrokeil/arangodb-php-client/blob/master/LICENSE.md New BSD License
8
 */
9
10
namespace ArangoDb\Statement;
11
12
use ArangoDb\Exception\NoCursorId;
13
use ArangoDb\Exception\ServerException;
14
use ArangoDb\Http\TypeSupport;
15
use ArangoDb\Type\CursorType;
16
use Countable;
17
use Fig\Http\Message\StatusCodeInterface;
18
use Iterator;
19
use Psr\Http\Client\ClientExceptionInterface;
20
21
/**
22
 * @implements Iterator<int, mixed>
23
 */
24
final class Statement implements QueryResult, Iterator, Countable
25
{
26
    /**
27
     * @var TypeSupport
28
     */
29
    private $client;
30
31
    /**
32
     * @var CursorType
33
     */
34
    private $cursor;
35
36
    /**
37
     * @var StreamHandler
38
     */
39
    private $streamHandler;
40
41
    /**
42
     * @var StreamHandlerFactoryInterface
43
     */
44
    private $streamHandlerFactory;
45
46
    /**
47
     * Number of HTTP calls that were made to build the cursor result
48
     *
49
     * @var int
50
     */
51
    private $fetches = 0;
52
53
    /**
54
     * Query is executed on first access
55
     *
56
     * @param TypeSupport $client - connection to be used
57
     * @param CursorType $cursor
58
     * @param StreamHandlerFactoryInterface $streamHandlerFactory
59
     */
60 2
    public function __construct(
61
        TypeSupport $client,
62
        CursorType $cursor,
63
        StreamHandlerFactoryInterface $streamHandlerFactory
64
    ) {
65 2
        $this->client = $client;
66 2
        $this->cursor = $cursor;
67 2
        $this->streamHandlerFactory = $streamHandlerFactory;
68 2
    }
69
70
    /**
71
     * Fetch outstanding results from the server
72
     *
73
     * @return void
74
     * @throws ClientExceptionInterface
75
     */
76 2
    private function fetchOutstanding(): void
77
    {
78 2
        if ($this->fetches === 0) {
79 2
            $request = $this->cursor;
80
        } else {
81
            $cursorId = $this->streamHandler->cursorId();
82
83
            if ($cursorId === null) {
84
                throw NoCursorId::forType($this->cursor);
85
            }
86
            $request = $this->cursor::nextBatch($cursorId);
87
        }
88 2
        $response = $this->client->sendType($request);
89
90 2
        $httpStatusCode = $response->getStatusCode();
91
92 2
        if ($httpStatusCode < StatusCodeInterface::STATUS_OK
93 2
            || $httpStatusCode > StatusCodeInterface::STATUS_MULTIPLE_CHOICES
94
        ) {
95
            throw ServerException::with($request, $response);
96
        }
97
98 2
        if ($this->fetches === 0) {
99 2
            $this->streamHandler = $this->streamHandlerFactory->createStreamHandler($response->getBody());
100
        } else {
101
            $this->streamHandler->appendStream($response->getBody());
102
        }
103
        $this->fetches++;
104
    }
105
106
    /**
107
     * Fetches next result from server and returns all current loaded results. Null if cursor end has reached.
108
     *
109
     * @return string|array|object|null Data
110
     * @throws ClientExceptionInterface
111
     */
112
    public function fetch()
113
    {
114
        if (null === $this->streamHandler || $this->streamHandler->hasMore()) {
115
            $this->fetchOutstanding();
116
            return $this->streamHandler->result();
117
        }
118
        return null;
119
    }
120
121
    /**
122
     * Fetches all results from server and returns overall result.
123
     * This might issue additional HTTP requests to fetch any outstanding results from the server.
124
     *
125
     * @return string|array|object Data
126
     * @throws ClientExceptionInterface
127
     */
128
    public function fetchAll()
129
    {
130
        while (null === $this->streamHandler || $this->streamHandler->hasMore()) {
131
            $this->fetchOutstanding();
132
        }
133
134
        return $this->streamHandler->completeResult();
135
    }
136
137
    public function resultCount(): ?int
138
    {
139
        if (null === $this->streamHandler) {
140
            $this->fetchOutstanding();
141
        }
142
        return $this->streamHandler->resultCount();
143
    }
144
145
    public function result()
146
    {
147
        if (null === $this->streamHandler) {
148
            $this->fetchOutstanding();
149
        }
150
        return $this->streamHandler->result();
151
    }
152
153
    /**
154
     * Get the total number of results in the cursor.
155
     *
156
     * This might issue additional HTTP requests to fetch any outstanding results from the server.
157
     *
158
     * @return int Total number of results
159
     * @throws ClientExceptionInterface
160
     */
161
    public function count()
162
    {
163
        if (null === $this->streamHandler) {
164
            $this->fetchOutstanding();
165
        }
166
167
        while ($this->streamHandler->hasMore()) {
168
            $this->fetchOutstanding();
169
        }
170
171
        return $this->streamHandler->count();
172
    }
173
174
    /**
175
     * Rewind the cursor, loads first batch, can be repeated (new cursor will be created)
176
     *
177
     * @return void
178
     * @throws ClientExceptionInterface
179
     */
180 2
    public function rewind()
181
    {
182 2
        $this->fetches = 0;
183 2
        $this->fetchOutstanding();
184
    }
185
186
    /**
187
     * Return the current result row depending on stream handler
188
     *
189
     * @return string|array|object Data
190
     */
191
    public function current()
192
    {
193
        if (null === $this->streamHandler) {
194
            $this->fetchOutstanding();
195
        }
196
        return $this->streamHandler->current();
197
    }
198
199
    public function key(): int
200
    {
201
        if (null === $this->streamHandler) {
202
            $this->fetchOutstanding();
203
        }
204
        return $this->streamHandler->key();
205
    }
206
207
    public function next(): void
208
    {
209
        $this->streamHandler->next();
210
    }
211
212
    /**
213
     * @return bool
214
     * @throws ClientExceptionInterface
215
     */
216
    public function valid(): bool
217
    {
218
        if (null === $this->streamHandler) {
219
            $this->fetchOutstanding();
220
        }
221
222
        if (true === $this->streamHandler->valid()) {
223
            return true;
224
        }
225
226
        if (! $this->streamHandler->hasMore() || $this->streamHandler->cursorId() === null) {
227
            return false;
228
        }
229
230
        // need to fetch additional results from the server
231
        $this->fetchOutstanding();
232
233
        return $this->streamHandler->valid();
234
    }
235
236
    /**
237
     * Returns the number of HTTP calls that were made to build the cursor result
238
     *
239
     * @return int
240
     */
241 2
    public function fetches(): int
242
    {
243 2
        return $this->fetches;
244
    }
245
246
    public function cursorId(): ?string
247
    {
248
        if (null === $this->streamHandler) {
249
            $this->fetchOutstanding();
250
        }
251
        return $this->streamHandler->cursorId();
252
    }
253
254
    public function hasMore(): bool
255
    {
256
        if (null === $this->streamHandler) {
257
            $this->fetchOutstanding();
258
        }
259
        return $this->streamHandler->hasMore();
260
    }
261
262
    public function warnings(): array
263
    {
264
        if (null === $this->streamHandler) {
265
            $this->fetchOutstanding();
266
        }
267
        return $this->streamHandler->warnings();
268
    }
269
270
    public function fullCount(): ?int
271
    {
272
        if (null === $this->streamHandler) {
273
            $this->fetchOutstanding();
274
        }
275
        return $this->streamHandler->fullCount();
276
    }
277
278
    public function isCached(): bool
279
    {
280
        if (null === $this->streamHandler) {
281
            $this->fetchOutstanding();
282
        }
283
        return $this->streamHandler->isCached();
284
    }
285
286
    public function writesExecuted(): ?int
287
    {
288
        if (null === $this->streamHandler) {
289
            $this->fetchOutstanding();
290
        }
291
        return $this->streamHandler->writesExecuted();
292
    }
293
294
    public function writesIgnored(): ?int
295
    {
296
        if (null === $this->streamHandler) {
297
            $this->fetchOutstanding();
298
        }
299
        return $this->streamHandler->writesIgnored();
300
    }
301
302
    public function scannedFull(): ?int
303
    {
304
        if (null === $this->streamHandler) {
305
            $this->fetchOutstanding();
306
        }
307
        return $this->streamHandler->scannedFull();
308
    }
309
310
    public function scannedIndex(): ?int
311
    {
312
        if (null === $this->streamHandler) {
313
            $this->fetchOutstanding();
314
        }
315
        return $this->streamHandler->scannedIndex();
316
    }
317
318
    public function filtered(): ?int
319
    {
320
        if (null === $this->streamHandler) {
321
            $this->fetchOutstanding();
322
        }
323
        return $this->streamHandler->filtered();
324
    }
325
326
    /**
327
     * @return string|array|object Complete response body data
328
     */
329
    public function raw()
330
    {
331
        return $this->streamHandler->raw();
332
    }
333
}
334