Passed
Push — next ( 09b52e...659675 )
by Bas
13:30 queued 11:38
created

Statement::getWritesExecuted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
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
    /**
23
     * @var array<mixed>
24
     */
25
    protected array $results = [];
26
27
    protected ?stdClass $stats = null;
28
29
    /**
30
     * @var array<mixed>
31
     */
32
    protected array $warnings = [];
33
34
    protected ?int $cursorId = null;
35
36
    protected bool $cursorHasMoreResults = false;
37
38
    protected ?int $count = null;
39
40
    protected ?stdClass $extra = null;
41
42
    /**
43
     * Statement constructor.
44
     *
45
     * @param  ?array<mixed>  $bindVars
46
     * @param  array<mixed>  $options
47
     */
48 17
    public function __construct(protected ArangoClient $arangoClient, protected string $query, protected array|null $bindVars, protected array $options = [])
49
    {
50
    }
51
52
    /**
53
     * A statement can be used like an array to access the results.
54
     *
55
     * @return ArrayIterator<array-key, mixed>
56
     */
57 1
    public function getIterator(): ArrayIterator
58
    {
59 1
        return new ArrayIterator($this->results);
60
    }
61
62
    /**
63
     * @throws ArangoException
64
     */
65 11
    public function execute(): bool
66
    {
67 11
        $this->results = [];
68
69 11
        $bodyContent = $this->prepareQueryBodyContent();
70
71 11
        $options = [
72
            'body' => $bodyContent,
73
        ];
74 11
        $results = $this->arangoClient->transactionAwareRequest('post', '/_api/cursor', $options);
75
76 11
        $this->handleQueryResults($results);
77
78 11
        $this->requestOutstandingResults($bodyContent);
79
80 11
        return true;
81
    }
82
83
    /**
84
     * @return array<mixed>
85
     */
86 15
    protected function prepareQueryBodyContent(): array
87
    {
88 15
        $bodyContent = $this->options;
89 15
        $bodyContent['query'] = $this->query;
90 15
        if (! empty($this->bindVars)) {
91
            $bodyContent['bindVars'] = $this->bindVars;
92
        }
93
94 15
        return $bodyContent;
95
    }
96
97 13
    protected function handleQueryResults(stdClass $results): void
98
    {
99 13
        $this->results = array_merge($this->results, (array) $results->result);
100
101 13
        if (property_exists($results, 'extra') && $results->extra !== null) {
102 13
            $this->extra = (object) $results->extra;
103
        }
104
105 13
        if (property_exists($results, 'count') && $results->count !== null) {
106 1
            $this->count = (int) $results->count;
107
        }
108
109 13
        $this->cursorHasMoreResults = (bool) $results->hasMore;
110 13
        $this->cursorId = $results->hasMore ? (int) $results->id : null;
111
    }
112
113
    /**
114
     * @param  array<mixed>  $body
115
     *
116
     * @throws ArangoException
117
     */
118 13
    protected function requestOutstandingResults(array $body): void
119
    {
120 13
        while ($this->cursorHasMoreResults) {
121 1
            $uri = '/_api/cursor/'.(string) $this->cursorId;
122
123 1
            $options = [
124
                'body' => $body,
125
            ];
126
127 1
            $results = $this->arangoClient->request('put', $uri, $options);
128
129 1
            $this->handleQueryResults($results);
130
        }
131
    }
132
133
    /**
134
     * @throws ArangoException
135
     */
136 1
    public function explain(): stdClass
137
    {
138 1
        $body = $this->prepareQueryBodyContent();
139 1
        $options = [
140
            'body' => $body,
141
        ];
142
143 1
        return $this->arangoClient->request('post', '/_api/explain', $options);
144
    }
145
146
    /**
147
     * Parse and validate the query, will through an ArangoException if the query is invalid.
148
     *
149
     * @throws ArangoException
150
     */
151 1
    public function parse(): stdClass
152
    {
153 1
        $body = $this->prepareQueryBodyContent();
154 1
        $options = [
155
            'body' => $body,
156
        ];
157
158 1
        return $this->arangoClient->request('post', '/_api/query', $options);
159
    }
160
161
    /**
162
     * Execute the query and return performance information on the query.
163
     *
164
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
165
     *
166
     * @throws ArangoException
167
     */
168 2
    public function profile(int|bool $mode = 1): stdClass|null
169
    {
170 2
        $bodyContent = $this->prepareQueryBodyContent();
171
172 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
173 2
            $bodyContent['options'] = [];
174
        }
175 2
        $bodyContent['options']['profile'] = $mode;
176
177 2
        $options = [
178
            'body' => $bodyContent,
179
        ];
180
181 2
        $results = $this->arangoClient->request('post', '/_api/cursor', $options);
182
183 2
        $this->handleQueryResults($results);
184
185 2
        $this->requestOutstandingResults($bodyContent);
186
187 2
        return $this->extra;
188
    }
189
190 2
    public function setQuery(string $query): self
191
    {
192 2
        $this->query = $query;
193
194 2
        return $this;
195
    }
196
197 1
    public function getQuery(): string
198
    {
199 1
        return $this->query;
200
    }
201
202
    /**
203
     * Fetch all results.
204
     *
205
     * @return array<mixed>
206
     */
207 6
    public function fetchAll(): array
208
    {
209 6
        return $this->results;
210
    }
211
212
    /**
213
     * Return the total number of results. (not just the retrieved results)
214
     * Useful if not all results have been retrieved from the database yet.
215
     */
216 2
    public function getCount(): ?int
217
    {
218 2
        return $this->count;
219
    }
220
221 1
    public function getWritesExecuted(): int
222
    {
223 1
        return (int) $this->extra?->stats?->writesExecuted;
224
    }
225
}
226