Failed Conditions
Push — next ( 580d6f...1a59b4 )
by Bas
02:36
created

Statement::parse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 14
    public function __construct(
79
        ArangoClient $arangoClient,
80
        string $query,
81
        array $bindVars = null,
82
        array $options = []
83
    ) {
84 14
        $this->arangoClient = $arangoClient;
85 14
        $this->query = $query;
86 14
        $this->bindVars = $bindVars;
87 14
        $this->options = $options;
88 14
    }
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 8
    public function execute(): bool
105
    {
106 8
        $this->results = [];
107
108 8
        $bodyContent = $this->prepareQueryBodyContent();
109 8
        $body = $this->arangoClient->jsonEncode($bodyContent);
110
111 8
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', ['body' => $body]);
112
113 8
        $this->handleQueryResults($results);
114
115 8
        $this->requestOutstandingResults($body);
116
117 8
        return ! $results['error'];
118
    }
119
120
    /**
121
     * @return array<mixed>
122
     */
123 12
    protected function prepareQueryBodyContent(): array
124
    {
125 12
        $bodyContent = $this->options;
126 12
        $bodyContent['query'] = $this->query;
127 12
        if (! empty($this->bindVars)) {
128
            $bodyContent['bindVars'] = $this->bindVars;
129
        }
130
131 12
        return $bodyContent;
132
    }
133
134
    /**
135
     * @param  array<mixed>  $results
136
     */
137 10
    protected function handleQueryResults(array $results): void
138
    {
139 10
        $this->results = array_merge($this->results, (array) $results['result']);
140
141 10
        if (isset($results['extra'])) {
142 10
            $this->extra = (array) $results['extra'];
143
        }
144
145 10
        if (array_key_exists('count', $results)) {
146 1
            $this->count = (int) $results['count'];
147
        }
148
149 10
        $this->cursorHasMoreResults = (bool) $results['hasMore'];
150 10
        $this->cursorId = $results['hasMore'] ?  (int) $results['id'] : null;
151 10
    }
152
153
    /**
154
     * @param  string  $body
155
     * @throws ArangoException
156
     */
157 10
    protected function requestOutstandingResults(string $body): void
158
    {
159 10
        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 10
    }
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
        $results = $this->arangoClient->request('post', '/_api/explain', ['body' => $body]);
180
181 1
        return $this->sanitizeRequestMetadata($results);
182
    }
183
184
    /**
185
     * Parse and validate the query, will through an ArangoException if the query is invalid.
186
     *
187
     * @return array<mixed>
188
     * @throws ArangoException
189
     */
190 1
    public function parse(): array
191
    {
192 1
        $bodyContent = $this->prepareQueryBodyContent();
193 1
        $body = $this->arangoClient->jsonEncode($bodyContent);
194
195 1
        $results = $this->arangoClient->request('post', '/_api/query', ['body' => $body]);
196
197 1
        return $this->sanitizeRequestMetadata($results);
198
    }
199
200
201
    /**
202
     * Execute the query and return performance information on the query.
203
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
204
     *
205
     * @param  int|bool  $mode
206
     * @return array<mixed>
207
     * @throws ArangoException
208
     */
209 2
    public function profile($mode = 1): array
210
    {
211 2
        $bodyContent = $this->prepareQueryBodyContent();
212
213 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
214 2
            $bodyContent['options'] = [];
215
        }
216 2
        $bodyContent['options']['profile'] = $mode;
217
218 2
        $body = $this->arangoClient->jsonEncode($bodyContent);
219
220 2
        $results = $this->arangoClient->request('post', '/_api/cursor', ['body' => $body]);
221
222 2
        $this->handleQueryResults($results);
223
224 2
        $this->requestOutstandingResults($body);
225
226 2
        return $this->extra;
227
    }
228
229
230
    /**
231
     * Set a query on the statement
232
     *
233
     * @param  string  $query
234
     * @return Statement
235
     */
236 2
    public function setQuery(string $query): self
237
    {
238 2
        $this->query = $query;
239
240 2
        return $this;
241
    }
242
243
    /**
244
     * Get the statement's query.
245
     *
246
     * @return string
247
     */
248 1
    public function getQuery(): string
249
    {
250 1
        return $this->query;
251
    }
252
253
    /**
254
     * Fetch all results.
255
     *
256
     * @return array<mixed>
257
     */
258 4
    public function fetchAll(): array
259
    {
260 4
        return $this->results;
261
    }
262
263
    /**
264
     * Return the total number of results.
265
     * Useful if not all results have been retrieved from the database yet.
266
     *
267
     * @return int|null
268
     */
269 2
    public function getCount(): ?int
270
    {
271 2
        return $this->count;
272
    }
273
}
274