Connection   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 295
Duplicated Lines 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 68
dl 0
loc 295
rs 9.68
c 6
b 2
f 0
wmc 34

21 Methods

Rating   Name   Duplication   Size   Complexity  
A reconnect() 0 4 2
A getArangoClient() 0 3 1
A setDatabaseName() 0 9 2
A escapeArray() 0 12 3
A setArangoClient() 0 3 1
A aqb() 0 3 1
A getServerVersion() 0 9 2
A escapeBool() 0 3 2
A __construct() 0 16 2
A reconnectIfMissingConnection() 0 4 2
A disconnect() 0 3 1
A escapeBinary() 0 7 2
A getDatabaseName() 0 3 1
A threadCount() 0 7 2
A getDefaultPostProcessor() 0 3 1
A getDefaultQueryGrammar() 0 5 1
A getElapsedTime() 0 3 1
A getSchemaBuilder() 0 3 1
A getSchemaGrammar() 0 3 1
A escapeString() 0 19 4
A escape() 0 9 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent;
6
7
use ArangoClient\ArangoClient;
8
use Illuminate\Database\Connection as IlluminateConnection;
9
use Illuminate\Database\Schema\Grammars\Grammar as IlluminateGrammar;
10
use LaravelFreelancerNL\Aranguent\Concerns\DetectsDeadlocks;
11
use LaravelFreelancerNL\Aranguent\Concerns\DetectsLostConnections;
12
use LaravelFreelancerNL\Aranguent\Concerns\HandlesArangoDb;
13
use LaravelFreelancerNL\Aranguent\Concerns\ManagesTransactions;
14
use LaravelFreelancerNL\Aranguent\Concerns\RunsQueries;
15
use LaravelFreelancerNL\Aranguent\Query\Grammar as QueryGrammar;
16
use LaravelFreelancerNL\Aranguent\Query\Processor;
17
use LaravelFreelancerNL\Aranguent\Schema\Builder as SchemaBuilder;
18
use LaravelFreelancerNL\FluentAQL\QueryBuilder as ArangoQueryBuilder;
19
use RuntimeException;
20
use Spatie\DataTransferObject\Exceptions\UnknownProperties;
21
22
class Connection extends IlluminateConnection
23
{
24
    use HandlesArangoDb;
25
    use DetectsDeadlocks;
26
    use DetectsLostConnections;
27
    use ManagesTransactions;
28
    use RunsQueries;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aranguent\Concerns\RunsQueries requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Connection: $binds, $query
Loading history...
29
30
    /**
31
     * @var ArangoClient|null
32
     */
33
    protected $arangoClient;
34
35
    /**
36
     * The ArangoDB driver name.
37
     */
38
    protected string $driverName = 'arangodb';
39
40
    /**
41
     * Connection constructor.
42
     *
43
     * @param  array<mixed>  $config
44
     *
45
     * @throws UnknownProperties
46
     */
47
    public function __construct($config = [])
48
    {
49
        $this->config = $config;
50
51
        $this->database = (isset($this->config['database'])) ? $this->config['database'] : '';
52
        $this->tablePrefix = $this->config['tablePrefix'] ?? '';
53
54
        // activate and set the database client connection
55
        $this->arangoClient = new ArangoClient($this->config);
56
57
        // We need to initialize a query grammar and the query post processors
58
        // which are both very important parts of the database abstractions
59
        // so, we initialize these to their default values while starting.
60
        $this->useDefaultQueryGrammar();
61
62
        $this->useDefaultPostProcessor();
63
    }
64
65
    /**
66
     * Get a schema builder instance for the connection.
67
     */
68
    public function getSchemaBuilder(): SchemaBuilder
69
    {
70
        return new SchemaBuilder($this);
71
    }
72
73
    /**
74
     * Get the default query grammar instance.
75
     */
76
    //    protected function getDefaultQueryGrammar(): QueryGrammar
77
    //    {
78
    //        return new QueryGrammar();
79
    //    }
80
81
    /**
82
     * Get the default post processor instance.
83
     */
84
    protected function getDefaultPostProcessor(): Processor
85
    {
86
        return new Processor();
87
    }
88
89
    /**
90
     * Get the default query grammar instance.
91
     *
92
     * @return QueryGrammar
93
     */
94
    protected function getDefaultQueryGrammar()
95
    {
96
        $grammar = new QueryGrammar($this);
97
98
        return $grammar;
99
    }
100
101
    /**
102
     * Get the schema grammar used by the connection.
103
     *
104
     * @return IlluminateGrammar
105
     */
106
    public function getSchemaGrammar()
107
    {
108
        return $this->schemaGrammar;
109
    }
110
111
    /**
112
     * Disconnect from the underlying ArangoDB connection.
113
     *
114
     * @return void
115
     */
116
    public function disconnect()
117
    {
118
        $this->arangoClient = null;
119
    }
120
121
    /**
122
     * Reconnect to the database.
123
     *
124
     * @throws \LogicException
125
     */
126
    public function reconnect()
127
    {
128
        if (is_callable($this->reconnector)) {
129
            return call_user_func($this->reconnector, $this);
130
        }
131
    }
132
133
    /**
134
     * Reconnect to the database if an ArangoDB connection is missing.
135
     *
136
     * @return void
137
     */
138
    public function reconnectIfMissingConnection()
139
    {
140
        if (is_null($this->arangoClient)) {
141
            $this->reconnect();
142
        }
143
    }
144
145
    public function getArangoClient(): ArangoClient|null
146
    {
147
        return $this->arangoClient;
148
    }
149
150
    public function setArangoClient(ArangoClient $arangoClient): void
151
    {
152
        $this->arangoClient = $arangoClient;
153
    }
154
155
    /**
156
     * Set the name of the connected database.
157
     *
158
     * @param  string  $database
159
     * @return $this
160
     */
161
    public function setDatabaseName($database)
162
    {
163
        $this->database = $database;
164
165
        if ($this->arangoClient !== null) {
166
            $this->arangoClient->setDatabase($database);
167
        }
168
169
        return $this;
170
    }
171
172
    public function getDatabaseName(): string
173
    {
174
        return $this->database;
175
    }
176
177
    public static function aqb(): ArangoQueryBuilder
178
    {
179
        return new ArangoQueryBuilder();
180
    }
181
182
    /**
183
     * Escape a binary value for safe SQL embedding.
184
     *
185
     * @param  string  $value
186
     * @return string
187
     */
188
    protected function escapeBinary($value)
189
    {
190
        if (mb_detect_encoding($value, ['UTF-8'])) {
191
            return $value;
192
        }
193
194
        return base64_encode($value);
195
    }
196
197
    /**
198
     * Escape a value for safe SQL embedding.
199
     *
200
     * @param  array<mixed>|string|float|int|bool|null  $value
201
     * @param  bool  $binary
202
     * @return string
203
     *
204
     * @SuppressWarnings("PHPMD.BooleanArgumentFlag")
205
     */
206
    public function escape($value, $binary = false)
207
    {
208
        return match (gettype($value)) {
209
            'array' => $this->escapeArray($value),
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type boolean and double and integer and null and string; however, parameter $array of LaravelFreelancerNL\Aran...nnection::escapeArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

209
            'array' => $this->escapeArray(/** @scrutinizer ignore-type */ $value),
Loading history...
210
            'boolean' => $this->escapeBool($value),
211
            'double' => (string) $value,
212
            'integer' => (string) $value,
213
            'NULL' => 'null',
214
            default => $this->escapeString($value, $binary = false),
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array<mixed,mixed>; however, parameter $value of LaravelFreelancerNL\Aran...nection::escapeString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

214
            default => $this->escapeString(/** @scrutinizer ignore-type */ $value, $binary = false),
Loading history...
215
        };
216
    }
217
218
    /**
219
     * Escape a string value for safe SQL embedding.
220
     *
221
     * @param  string  $value
222
     * @return string
223
     *
224
     * @SuppressWarnings("PHPMD.BooleanArgumentFlag")
225
     */
226
    protected function escapeString($value, bool $binary = false)
227
    {
228
        if ($binary === true) {
229
            return $this->escapeBinary($value);
230
        }
231
232
        if (str_contains($value, "\00")) {
233
            throw new RuntimeException('Strings with null bytes cannot be escaped. Use the binary escape option.');
234
        }
235
236
        if (preg_match('//u', $value) === false) {
237
            throw new RuntimeException('Strings with invalid UTF-8 byte sequences cannot be escaped.');
238
        }
239
240
        return '"' . str_replace(
241
            ['\\', "\0", "\n", "\r", "'", '"', "\x1a"],
242
            ['\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'],
243
            $value,
244
        ) . '"';
245
    }
246
247
    /**
248
     * Escape an array value for safe SQL embedding.
249
     *
250
     * @param  array<mixed>  $array
251
     * @return string
252
     */
253
    protected function escapeArray(array $array): string
254
    {
255
        foreach ($array as $key => $value) {
256
            $array[$key] = $this->escape($value);
257
        }
258
259
        if (array_is_list($array)) {
260
            return '[' . implode(', ', $array) . ']';
261
        }
262
263
        $grammar = $this->getDefaultQueryGrammar();
264
        return $grammar->generateAqlObject($array);
265
    }
266
267
    /**
268
     * Escape a boolean value for safe SQL embedding.
269
     *
270
     * @param  bool  $value
271
     * @return string
272
     */
273
    protected function escapeBool($value)
274
    {
275
        return $value ? 'true' : 'false';
276
    }
277
278
    /**
279
     * Get the elapsed time since a given starting point.
280
     *
281
     * @param  int|float  $start
282
     * @return float
283
     */
284
    protected function getElapsedTime($start)
285
    {
286
        return round((microtime(true) - $start) * 1000, 2);
287
    }
288
289
    /**
290
     * Get the number of open connections for the database.
291
     *
292
     * @return int|null
293
     */
294
    public function threadCount()
295
    {
296
        if (!$this->arangoClient) {
297
            return null;
298
        }
299
300
        return $this->arangoClient->monitor()->getCurrentConnections();
301
    }
302
303
    /**
304
     * Get the server version for the connection.
305
     *
306
     * @return string
307
     */
308
    public function getServerVersion(): string
309
    {
310
        if (!$this->arangoClient) {
311
            return '';
312
        }
313
314
        $rawVersion = $this->arangoClient->admin()->getVersion();
315
316
        return $rawVersion->version;
317
    }
318
}
319