Passed
Push — next ( d904ac...c0af08 )
by Bas
13:43
created

Statement::requestOutstandingResults()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 12
ccs 6
cts 6
cp 1
crap 2
rs 10
1
<?php
2
3
namespace ArangoClient\Statement;
4
5
use ArangoClient\ArangoClient;
6
use ArangoClient\Exceptions\ArangoException;
7
use ArangoClient\Manager;
8
use ArrayIterator;
9
use IteratorAggregate;
10
11
/**
12
 * Executes queries on ArangoDB
13
 *
14
 * @see https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
15
 *
16
 * @package ArangoClient
17
 *
18
 * @template-implements \IteratorAggregate<mixed>
19
 */
20
class Statement extends Manager implements IteratorAggregate
21
{
22
    protected ArangoClient $arangoClient;
23
24
    protected string $query;
25
26
    /**
27
     * @var array<scalar>|null
28
     */
29
    protected ?array $bindVars;
30
31
    /**
32
     * @var array<mixed>
33
     */
34
    protected array $options = [];
35
36
    /**
37
     * @var array<mixed>
38
     */
39
    protected array $results = [];
40
41
    /**
42
     * @var array<mixed>
43
     */
44
    protected array $stats = [];
45
46
    /**
47
     * @var array<mixed>
48
     */
49
    protected array $warnings = [];
50
51
    /**
52
     * @var int|null
53
     */
54
    protected ?int $cursorId = null;
55
56
    /**
57
     * @var bool
58
     */
59
    protected bool $cursorHasMoreResults = false;
60
61
    /**
62
     * @var int|null
63
     */
64
    protected ?int $count = null;
65
66
    /**
67
     * @var array<mixed>
68
     */
69
    protected array $extra = [];
70
71
    /**
72
     * Statement constructor.
73
     * @param  ArangoClient  $arangoClient
74
     * @param  string  $query
75
     * @param  array<scalar>  $bindVars
76
     * @param  array<mixed>  $options
77
     */
78 16
    public function __construct(
79
        ArangoClient $arangoClient,
80
        string $query,
81
        array $bindVars = null,
82
        array $options = []
83
    ) {
84 16
        $this->arangoClient = $arangoClient;
85 16
        $this->query = $query;
86 16
        $this->bindVars = $bindVars;
87 16
        $this->options = $options;
88 16
    }
89
90
    /**
91
     * A statement can be used like an array to access the results.
92
     *
93
     * @return ArrayIterator<array-key, mixed>
94
     */
95 1
    public function getIterator(): ArrayIterator
96
    {
97 1
        return new ArrayIterator($this->results);
98
    }
99
100
    /**
101
     * @return bool
102
     * @throws ArangoException
103
     */
104 10
    public function execute(): bool
105
    {
106 10
        $this->results = [];
107
108 10
        $bodyContent = $this->prepareQueryBodyContent();
109
110
        $options = [
111 10
            'body' => $bodyContent
112
        ];
113 10
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', $options);
114
115 10
        $this->handleQueryResults($results);
116
117 10
        $this->requestOutstandingResults($bodyContent);
118
119 10
        return true;
120
    }
121
122
    /**
123
     * @return array<mixed>
124
     */
125 14
    protected function prepareQueryBodyContent(): array
126
    {
127 14
        $bodyContent = $this->options;
128 14
        $bodyContent['query'] = $this->query;
129 14
        if (! empty($this->bindVars)) {
130
            $bodyContent['bindVars'] = $this->bindVars;
131
        }
132
133 14
        return $bodyContent;
134
    }
135
136
    /**
137
     * @param  array<mixed>  $results
138
     */
139 12
    protected function handleQueryResults(array $results): void
140
    {
141 12
        $this->results = array_merge($this->results, (array) $results['result']);
142
143 12
        if (isset($results['extra'])) {
144 12
            $this->extra = (array) $results['extra'];
145
        }
146
147 12
        if (array_key_exists('count', $results)) {
148 1
            $this->count = (int) $results['count'];
149
        }
150
151 12
        $this->cursorHasMoreResults = (bool) $results['hasMore'];
152 12
        $this->cursorId = $results['hasMore'] ?  (int) $results['id'] : null;
153 12
    }
154
155
    /**
156
     * @param  array<mixed>  $body
157
     * @throws ArangoException
158
     */
159 12
    protected function requestOutstandingResults(array $body): void
160
    {
161 12
        while ($this->cursorHasMoreResults) {
162 1
            $uri = '/_api/cursor/' . (string) $this->cursorId;
163
164
            $options = [
165 1
                'body' => $body
166
            ];
167
168 1
            $results = $this->arangoClient->request('put', $uri, $options);
169
170 1
            $this->handleQueryResults($results);
171
        }
172 12
    }
173
174
    /**
175
     * Explain the given query
176
     *
177
     * @return array<mixed>
178
     * @throws ArangoException
179
     */
180 1
    public function explain(): array
181
    {
182 1
        $body = $this->prepareQueryBodyContent();
183
        $options = [
184 1
            'body' => $body
185
        ];
186
187 1
        return $this->arangoClient->request('post', '/_api/explain', $options);
188
    }
189
190
    /**
191
     * Parse and validate the query, will through an ArangoException if the query is invalid.
192
     *
193
     * @return array<mixed>
194
     * @throws ArangoException
195
     */
196 1
    public function parse(): array
197
    {
198 1
        $body = $this->prepareQueryBodyContent();
199
        $options = [
200 1
            'body' => $body
201
        ];
202
203 1
        return $this->arangoClient->request('post', '/_api/query', $options);
204
    }
205
206
    /**
207
     * Execute the query and return performance information on the query.
208
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
209
     *
210
     * @param  int|bool  $mode
211
     * @return array<mixed>
212
     * @throws ArangoException
213
     */
214 2
    public function profile($mode = 1): array
215
    {
216 2
        $bodyContent = $this->prepareQueryBodyContent();
217
218 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
219 2
            $bodyContent['options'] = [];
220
        }
221 2
        $bodyContent['options']['profile'] = $mode;
222
223
        $options = [
224 2
            'body' => $bodyContent
225
        ];
226
227 2
        $results = $this->arangoClient->request('post', '/_api/cursor', $options);
228
229 2
        $this->handleQueryResults($results);
230
231 2
        $this->requestOutstandingResults($bodyContent);
232
233 2
        return $this->extra;
234
    }
235
236
    /**
237
     * Set a query on the statement
238
     *
239
     * @param  string  $query
240
     * @return Statement
241
     */
242 2
    public function setQuery(string $query): self
243
    {
244 2
        $this->query = $query;
245
246 2
        return $this;
247
    }
248
249
    /**
250
     * Get the statement's query.
251
     *
252
     * @return string
253
     */
254 1
    public function getQuery(): string
255
    {
256 1
        return $this->query;
257
    }
258
259
    /**
260
     * Fetch all results.
261
     *
262
     * @return array<mixed>
263
     */
264 5
    public function fetchAll(): array
265
    {
266 5
        return $this->results;
267
    }
268
269
    /**
270
     * Return the total number of results.
271
     * Useful if not all results have been retrieved from the database yet.
272
     *
273
     * @return int|null
274
     */
275 2
    public function getCount(): ?int
276
    {
277 2
        return $this->count;
278
    }
279
280
    /**
281
     * Return the number writes executed to by the query.
282
     *
283
     * @return int
284
     */
285 1
    public function getWritesExecuted(): int
286
    {
287 1
        return (isset($this->extra['stats']['writesExecuted'])) ? (int) $this->extra['stats']['writesExecuted'] : 0;
288
    }
289
}
290