Passed
Push — next ( 147fd5...b42a44 )
by Bas
03:36 queued 01:41
created

Statement::prepareQueryBodyContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
crap 2.0185
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
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
 * @package ArangoClient
18
 *
19
 * @template-implements \IteratorAggregate<mixed>
20
 */
21
class Statement extends Manager implements IteratorAggregate
22
{
23
    protected ArangoClient $arangoClient;
24
25
    protected string $query;
26
27
    /**
28
     * @var array<scalar>|null
29
     */
30
    protected ?array $bindVars;
31
32
    /**
33
     * @var array<mixed>
34
     */
35
    protected array $options = [];
36
37
    /**
38
     * @var array<mixed>
39
     */
40
    protected array $results = [];
41
42
    protected ?stdClass $stats = null;
43
44
    /**
45
     * @var array<mixed>
46
     */
47
    protected array $warnings = [];
48
49
    protected ?int $cursorId = null;
50
51
    protected bool $cursorHasMoreResults = false;
52
53
    protected ?int $count = null;
54
55
    protected ?stdClass $extra = null;
56
57
    /**
58
     * Statement constructor.
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 17
    }
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 11
            '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 13
    }
136
137
    /**
138
     * @param  array<mixed>  $body
139
     * @throws ArangoException
140
     */
141 13
    protected function requestOutstandingResults(array $body): void
142
    {
143 13
        while ($this->cursorHasMoreResults) {
144 1
            $uri = '/_api/cursor/' . (string) $this->cursorId;
145
146 1
            $options = [
147 1
                'body' => $body
148
            ];
149
150 1
            $results = $this->arangoClient->request('put', $uri, $options);
151
152 1
            $this->handleQueryResults($results);
153
        }
154 13
    }
155
156
    /**
157
     * @throws ArangoException
158
     */
159 1
    public function explain(): stdClass
160
    {
161 1
        $body = $this->prepareQueryBodyContent();
162 1
        $options = [
163 1
            'body' => $body
164
        ];
165
166 1
        return $this->arangoClient->request('post', '/_api/explain', $options);
167
    }
168
169
    /**
170
     * Parse and validate the query, will through an ArangoException if the query is invalid.
171
     *
172
     * @throws ArangoException
173
     */
174 1
    public function parse(): stdClass
175
    {
176 1
        $body = $this->prepareQueryBodyContent();
177 1
        $options = [
178 1
            'body' => $body
179
        ];
180
181 1
        return $this->arangoClient->request('post', '/_api/query', $options);
182
    }
183
184
    /**
185
     * Execute the query and return performance information on the query.
186
     * @see https://www.arangodb.com/docs/3.7/aql/execution-and-performance-query-profiler.html
187
     *
188
     * @throws ArangoException
189
     */
190 2
    public function profile(int|bool $mode = 1): stdClass|null
0 ignored issues
show
Bug introduced by
The type ArangoClient\Statement\null was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
191
    {
192 2
        $bodyContent = $this->prepareQueryBodyContent();
193
194 2
        if (! isset($bodyContent['options']) || ! is_array($bodyContent['options'])) {
195 2
            $bodyContent['options'] = [];
196
        }
197 2
        $bodyContent['options']['profile'] = $mode;
198
199 2
        $options = [
200 2
            'body' => $bodyContent
201
        ];
202
203 2
        $results = $this->arangoClient->request('post', '/_api/cursor', $options);
204
205 2
        $this->handleQueryResults($results);
206
207 2
        $this->requestOutstandingResults($bodyContent);
208
209 2
        return $this->extra;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->extra could return the type null which is incompatible with the type-hinted return ArangoClient\Statement\null|stdClass. Consider adding an additional type-check to rule them out.
Loading history...
210
    }
211
212 2
    public function setQuery(string $query): self
213
    {
214 2
        $this->query = $query;
215
216 2
        return $this;
217
    }
218
219 1
    public function getQuery(): string
220
    {
221 1
        return $this->query;
222
    }
223
224
    /**
225
     * Fetch all results.
226
     *
227
     * @return array<mixed>
228
     */
229 6
    public function fetchAll(): array
230
    {
231 6
        return $this->results;
232
    }
233
234
    /**
235
     * Return the total number of results. (not just the retrieved results)
236
     * Useful if not all results have been retrieved from the database yet.
237
     */
238 2
    public function getCount(): ?int
239
    {
240 2
        return $this->count;
241
    }
242
243 1
    public function getWritesExecuted(): int
244
    {
245 1
        if (! isset($this->extra->stats) || ! is_object($this->extra->stats)) {
246
            return 0;
247
        }
248
249 1
        return (isset($this->extra->stats->writesExecuted)) ? (int) $this->extra->stats->writesExecuted : 0;
250
    }
251
}
252