Passed
Push — next ( c138b3...560f45 )
by Bas
12:33 queued 10:38
created

Statement   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Test Coverage

Coverage 96.97%

Importance

Changes 0
Metric Value
eloc 69
c 0
b 0
f 0
dl 0
loc 232
ccs 64
cts 66
cp 0.9697
rs 10
wmc 24

14 Methods

Rating   Name   Duplication   Size   Complexity  
A prepareQueryBodyContent() 0 9 2
A getIterator() 0 3 1
A __construct() 0 10 1
A handleQueryResults() 0 14 4
A execute() 0 16 1
A parse() 0 8 1
A getQuery() 0 3 1
A requestOutstandingResults() 0 12 2
A setQuery() 0 5 1
A fetchAll() 0 3 1
A getWritesExecuted() 0 7 4
A profile() 0 20 3
A explain() 0 8 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
use stdClass;
11
12
/**
13
 * Executes queries on ArangoDB
14
 *
15
 * @see https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
16
 *
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
    protected ?stdClass $stats = null;
42
43
    /**
44
     * @var array<mixed>
45
     */
46
    protected array $warnings = [];
47
48
    protected ?int $cursorId = null;
49
50
    protected bool $cursorHasMoreResults = false;
51
52
    protected ?int $count = null;
53
54
    protected ?stdClass $extra = null;
55
56
    /**
57
     * Statement constructor.
58
     *
59
     * @param  ArangoClient  $arangoClient
60
     * @param  string  $query
61
     * @param  array<scalar>  $bindVars
62
     * @param  array<mixed>  $options
63
     */
64 17
    public function __construct(
65
        ArangoClient $arangoClient,
66
        string $query,
67
        array $bindVars = null,
68
        array $options = []
69
    ) {
70 17
        $this->arangoClient = $arangoClient;
71 17
        $this->query = $query;
72 17
        $this->bindVars = $bindVars;
73 17
        $this->options = $options;
74
    }
75
76
    /**
77
     * A statement can be used like an array to access the results.
78
     *
79
     * @return ArrayIterator<array-key, mixed>
80
     */
81 1
    public function getIterator(): ArrayIterator
82
    {
83 1
        return new ArrayIterator($this->results);
84
    }
85
86
    /**
87
     * @throws ArangoException
88
     */
89 11
    public function execute(): bool
90
    {
91 11
        $this->results = [];
92
93 11
        $bodyContent = $this->prepareQueryBodyContent();
94
95 11
        $options = [
96
            'body' => $bodyContent,
97
        ];
98 11
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', $options);
99
100 11
        $this->handleQueryResults($results);
101
102 11
        $this->requestOutstandingResults($bodyContent);
103
104 11
        return true;
105
    }
106
107
    /**
108
     * @return array<mixed>
109
     */
110 15
    protected function prepareQueryBodyContent(): array
111
    {
112 15
        $bodyContent = $this->options;
113 15
        $bodyContent['query'] = $this->query;
114 15
        if (! empty($this->bindVars)) {
115
            $bodyContent['bindVars'] = $this->bindVars;
116
        }
117
118 15
        return $bodyContent;
119
    }
120
121 13
    protected function handleQueryResults(stdClass $results): void
122
    {
123 13
        $this->results = array_merge($this->results, (array) $results->result);
124
125 13
        if (isset($results->extra)) {
126 13
            $this->extra = (object) $results->extra;
127
        }
128
129 13
        if (isset($results->count)) {
130 1
            $this->count = (int) $results->count;
131
        }
132
133 13
        $this->cursorHasMoreResults = (bool) $results->hasMore;
134 13
        $this->cursorId = $results->hasMore ? (int) $results->id : null;
135
    }
136
137
    /**
138
     * @param  array<mixed>  $body
139
     *
140
     * @throws ArangoException
141
     */
142 13
    protected function requestOutstandingResults(array $body): void
143
    {
144 13
        while ($this->cursorHasMoreResults) {
145 1
            $uri = '/_api/cursor/'.(string) $this->cursorId;
146
147 1
            $options = [
148
                'body' => $body,
149
            ];
150
151 1
            $results = $this->arangoClient->request('put', $uri, $options);
152
153 1
            $this->handleQueryResults($results);
154
        }
155
    }
156
157
    /**
158
     * @throws ArangoException
159
     */
160 1
    public function explain(): stdClass
161
    {
162 1
        $body = $this->prepareQueryBodyContent();
163 1
        $options = [
164
            'body' => $body,
165
        ];
166
167 1
        return $this->arangoClient->request('post', '/_api/explain', $options);
168
    }
169
170
    /**
171
     * Parse and validate the query, will through an ArangoException if the query is invalid.
172
     *
173
     * @throws ArangoException
174
     */
175 1
    public function parse(): stdClass
176
    {
177 1
        $body = $this->prepareQueryBodyContent();
178 1
        $options = [
179
            'body' => $body,
180
        ];
181
182 1
        return $this->arangoClient->request('post', '/_api/query', $options);
183
    }
184
185
    /**
186
     * Execute the query and return performance information on the query.
187
     *
188
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
189
     *
190
     * @throws ArangoException
191
     */
192 2
    public function profile(int|bool $mode = 1): stdClass|null
193
    {
194 2
        $bodyContent = $this->prepareQueryBodyContent();
195
196 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
197 2
            $bodyContent['options'] = [];
198
        }
199 2
        $bodyContent['options']['profile'] = $mode;
200
201 2
        $options = [
202
            'body' => $bodyContent,
203
        ];
204
205 2
        $results = $this->arangoClient->request('post', '/_api/cursor', $options);
206
207 2
        $this->handleQueryResults($results);
208
209 2
        $this->requestOutstandingResults($bodyContent);
210
211 2
        return $this->extra;
212
    }
213
214 2
    public function setQuery(string $query): self
215
    {
216 2
        $this->query = $query;
217
218 2
        return $this;
219
    }
220
221 1
    public function getQuery(): string
222
    {
223 1
        return $this->query;
224
    }
225
226
    /**
227
     * Fetch all results.
228
     *
229
     * @return array<mixed>
230
     */
231 6
    public function fetchAll(): array
232
    {
233 6
        return $this->results;
234
    }
235
236
    /**
237
     * Return the total number of results. (not just the retrieved results)
238
     * Useful if not all results have been retrieved from the database yet.
239
     */
240 2
    public function getCount(): ?int
241
    {
242 2
        return $this->count;
243
    }
244
245 1
    public function getWritesExecuted(): int
246
    {
247 1
        if (! isset($this->extra->stats) || ! is_object($this->extra->stats)) {
248
            return 0;
249
        }
250
251 1
        return (isset($this->extra->stats->writesExecuted)) ? (int) $this->extra->stats->writesExecuted : 0;
252
    }
253
}
254