Failed Conditions
Push — next ( 1e4ade...f47ec0 )
by Bas
04:16 queued 11s
created

Statement   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 258
Duplicated Lines 0 %

Test Coverage

Coverage 98.48%

Importance

Changes 0
Metric Value
eloc 61
c 0
b 0
f 0
dl 0
loc 258
ccs 65
cts 66
cp 0.9848
rs 10
wmc 22

14 Methods

Rating   Name   Duplication   Size   Complexity  
A execute() 0 14 1
A prepareQueryBodyContent() 0 9 2
A requestOutstandingResults() 0 8 2
A getIterator() 0 3 1
A __construct() 0 10 1
A handleQueryResults() 0 14 4
A parse() 0 6 1
A getQuery() 0 3 1
A setQuery() 0 5 1
A fetchAll() 0 3 1
A getWritesExecuted() 0 3 2
A profile() 0 18 3
A explain() 0 6 1
A getCount() 0 3 1
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 10
        $body = $this->arangoClient->jsonEncode($bodyContent);
110
111 10
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', ['body' => $body]);
112
113 10
        $this->handleQueryResults($results);
114
115 10
        $this->requestOutstandingResults($body);
116
117 10
        return true;
118
    }
119
120
    /**
121
     * @return array<mixed>
122
     */
123 14
    protected function prepareQueryBodyContent(): array
124
    {
125 14
        $bodyContent = $this->options;
126 14
        $bodyContent['query'] = $this->query;
127 14
        if (! empty($this->bindVars)) {
128
            $bodyContent['bindVars'] = $this->bindVars;
129
        }
130
131 14
        return $bodyContent;
132
    }
133
134
    /**
135
     * @param  array<mixed>  $results
136
     */
137 12
    protected function handleQueryResults(array $results): void
138
    {
139 12
        $this->results = array_merge($this->results, (array) $results['result']);
140
141 12
        if (isset($results['extra'])) {
142 12
            $this->extra = (array) $results['extra'];
143
        }
144
145 12
        if (array_key_exists('count', $results)) {
146 1
            $this->count = (int) $results['count'];
147
        }
148
149 12
        $this->cursorHasMoreResults = (bool) $results['hasMore'];
150 12
        $this->cursorId = $results['hasMore'] ?  (int) $results['id'] : null;
151 12
    }
152
153
    /**
154
     * @param  string  $body
155
     * @throws ArangoException
156
     */
157 12
    protected function requestOutstandingResults(string $body): void
158
    {
159 12
        while ($this->cursorHasMoreResults) {
160 1
            $uri = '/_api/cursor/' . (string) $this->cursorId;
161
162 1
            $results = $this->arangoClient->request('put', $uri, ['body' => $body]);
163
164 1
            $this->handleQueryResults($results);
165
        }
166 12
    }
167
168
    /**
169
     * Explain the given query
170
     *
171
     * @return array<mixed>
172
     * @throws ArangoException
173
     */
174 1
    public function explain(): array
175
    {
176 1
        $bodyContent = $this->prepareQueryBodyContent();
177 1
        $body = $this->arangoClient->jsonEncode($bodyContent);
178
179 1
        return $this->arangoClient->request('post', '/_api/explain', ['body' => $body]);
180
    }
181
182
    /**
183
     * Parse and validate the query, will through an ArangoException if the query is invalid.
184
     *
185
     * @return array<mixed>
186
     * @throws ArangoException
187
     */
188 1
    public function parse(): array
189
    {
190 1
        $bodyContent = $this->prepareQueryBodyContent();
191 1
        $body = $this->arangoClient->jsonEncode($bodyContent);
192
193 1
        return $this->arangoClient->request('post', '/_api/query', ['body' => $body]);
194
    }
195
196
197
    /**
198
     * Execute the query and return performance information on the query.
199
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
200
     *
201
     * @param  int|bool  $mode
202
     * @return array<mixed>
203
     * @throws ArangoException
204
     */
205 2
    public function profile($mode = 1): array
206
    {
207 2
        $bodyContent = $this->prepareQueryBodyContent();
208
209 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
210 2
            $bodyContent['options'] = [];
211
        }
212 2
        $bodyContent['options']['profile'] = $mode;
213
214 2
        $body = $this->arangoClient->jsonEncode($bodyContent);
215
216 2
        $results = $this->arangoClient->request('post', '/_api/cursor', ['body' => $body]);
217
218 2
        $this->handleQueryResults($results);
219
220 2
        $this->requestOutstandingResults($body);
221
222 2
        return $this->extra;
223
    }
224
225
226
    /**
227
     * Set a query on the statement
228
     *
229
     * @param  string  $query
230
     * @return Statement
231
     */
232 2
    public function setQuery(string $query): self
233
    {
234 2
        $this->query = $query;
235
236 2
        return $this;
237
    }
238
239
    /**
240
     * Get the statement's query.
241
     *
242
     * @return string
243
     */
244 1
    public function getQuery(): string
245
    {
246 1
        return $this->query;
247
    }
248
249
    /**
250
     * Fetch all results.
251
     *
252
     * @return array<mixed>
253
     */
254 5
    public function fetchAll(): array
255
    {
256 5
        return $this->results;
257
    }
258
259
    /**
260
     * Return the total number of results.
261
     * Useful if not all results have been retrieved from the database yet.
262
     *
263
     * @return int|null
264
     */
265 2
    public function getCount(): ?int
266
    {
267 2
        return $this->count;
268
    }
269
270
    /**
271
     * Return the number writes executed to by the query.
272
     *
273
     * @return int
274
     */
275 1
    public function getWritesExecuted(): int
276
    {
277 1
        return (isset($this->extra['stats']['writesExecuted'])) ? (int) $this->extra['stats']['writesExecuted'] : 0;
278
    }
279
}
280