Passed
Push — next ( 5d094e...e6f75f )
by Bas
06:25 queued 03:06
created

Connection::getTablePrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 1
b 0
f 0
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
    protected ?ArangoClient $arangoClient = null;
31
32
    /**
33
     * The ArangoDB driver name.
34
     */
35
    protected string $driverName = 'arangodb';
36
37
    /**
38
     * Connection constructor.
39
     *
40
     * @param  array<mixed>  $config
41
     *
42
     * @throws UnknownProperties
43
     */
44 351
    public function __construct($config = [])
45
    {
46 351
        $this->config = $config;
47
48 351
        $this->database = (isset($this->config['database'])) ? $this->config['database'] : '';
49 351
        $this->tablePrefix = $this->config['tablePrefix'] ?? '';
50
51
        // activate and set the database client connection
52 351
        $this->arangoClient = new ArangoClient($this->config);
53
54
        // We need to initialize a query grammar and the query post processors
55
        // which are both very important parts of the database abstractions
56
        // so, we initialize these to their default values while starting.
57 351
        $this->useDefaultQueryGrammar();
58
59 351
        $this->useDefaultPostProcessor();
60
    }
61
62
    /**
63
     * Get a schema builder instance for the connection.
64
     */
65 55
    public function getSchemaBuilder(): SchemaBuilder
66
    {
67 55
        return new SchemaBuilder($this);
68
    }
69
70
    /**
71
     * Get the default query grammar instance.
72
     */
73
    //    protected function getDefaultQueryGrammar(): QueryGrammar
74
    //    {
75
    //        return new QueryGrammar();
76
    //    }
77
78
    /**
79
     * Get the default post processor instance.
80
     */
81 351
    protected function getDefaultPostProcessor(): Processor
82
    {
83 351
        return new Processor();
84
    }
85
86
    /**
87
     * Get the default query grammar instance.
88
     *
89
     * @return QueryGrammar
90
     */
91 351
    protected function getDefaultQueryGrammar()
92
    {
93 351
        ($grammar = new QueryGrammar())->setConnection($this);
94
95 351
        return $grammar;
96
    }
97
98
    /**
99
     * Get the schema grammar used by the connection.
100
     *
101
     * @return IlluminateGrammar
102
     */
103 55
    public function getSchemaGrammar()
104
    {
105 55
        return $this->schemaGrammar;
106
    }
107
108
    /**
109
     * Disconnect from the underlying ArangoDB connection.
110
     *
111
     * @return void
112
     */
113 110
    public function disconnect()
114
    {
115 110
        $this->arangoClient = null;
116
    }
117
118
    /**
119
     * Reconnect to the database.
120
     *
121
     * @throws \LogicException
122
     */
123 3
    public function reconnect()
124
    {
125 3
        if (is_callable($this->reconnector)) {
126 3
            return call_user_func($this->reconnector, $this);
127
        }
128
    }
129
130
    /**
131
     * Reconnect to the database if an ArangoDB connection is missing.
132
     *
133
     * @return void
134
     */
135 239
    public function reconnectIfMissingConnection()
136
    {
137 239
        if (is_null($this->arangoClient)) {
138
            $this->reconnect();
139
        }
140
    }
141
142 77
    public function getArangoClient(): ArangoClient|null
143
    {
144 77
        return $this->arangoClient;
145
    }
146
147
    /**
148
     * Set the name of the connected database.
149
     *
150
     * @param  string  $database
151
     * @return $this
152
     */
153 2
    public function setDatabaseName($database)
154
    {
155 2
        $this->database = $database;
156
157 2
        if ($this->arangoClient !== null) {
158 2
            $this->arangoClient->setDatabase($database);
159
        }
160
161 2
        return $this;
162
    }
163
164 2
    public function getDatabaseName(): string
165
    {
166 2
        return $this->database;
167
    }
168
169
    public static function aqb(): ArangoQueryBuilder
170
    {
171
        return new ArangoQueryBuilder();
172
    }
173
174
    /**
175
     * Escape a binary value for safe SQL embedding.
176
     *
177
     * @param  string  $value
178
     * @return string
179
     */
180
    protected function escapeBinary($value)
181
    {
182
        if (mb_detect_encoding($value, ['UTF-8'])) {
183
            return $value;
184
        }
185
186
        return base64_encode($value);
187
    }
188
189
    /**
190
     * Escape a value for safe SQL embedding.
191
     *
192
     * @param  array<mixed>|string|float|int|bool|null  $value
193
     * @param  bool  $binary
194
     * @return string
195
     *
196
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
197
     */
198 4
    public function escape($value, $binary = false)
199
    {
200 4
        return match(gettype($value)) {
201 4
            '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

201
            'array' => $this->escapeArray(/** @scrutinizer ignore-type */ $value),
Loading history...
202 4
            'boolean' => $this->escapeBool($value),
203 4
            'double' => (string) $value,
204 4
            'integer' => (string) $value,
205 4
            'NULL' => 'null',
206 4
            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

206
            default => $this->escapeString(/** @scrutinizer ignore-type */ $value, $binary = false),
Loading history...
207 4
        };
208
    }
209
210
    /**
211
     * Escape a string value for safe SQL embedding.
212
     *
213
     * @param  string  $value
214
     * @return string
215
     *
216
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
217
     */
218 4
    protected function escapeString($value, bool $binary = false)
219
    {
220 4
        if ($binary === true) {
221
            return $this->escapeBinary($value);
222
        }
223
224 4
        if (str_contains($value, "\00")) {
225
            throw new RuntimeException('Strings with null bytes cannot be escaped. Use the binary escape option.');
226
        }
227
228 4
        if (preg_match('//u', $value) === false) {
229
            throw new RuntimeException('Strings with invalid UTF-8 byte sequences cannot be escaped.');
230
        }
231
232 4
        return '"' . str_replace(
233 4
            ['\\', "\0", "\n", "\r", "'", '"', "\x1a"],
234 4
            ['\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'],
235 4
            $value
236 4
        ) . '"';
237
    }
238
239
    /**
240
     * Escape an array value for safe SQL embedding.
241
     *
242
     * @param  array<mixed>  $array
243
     * @return string
244
     */
245
    protected function escapeArray(array $array): string
246
    {
247
        foreach($array as $key => $value) {
248
            $array[$key] = $this->escape($value);
249
        }
250
251
        if (array_is_list($array)) {
252
            return '[' . implode(', ', $array) . ']';
253
        }
254
255
        $grammar = $this->getDefaultQueryGrammar();
256
        return $grammar->generateAqlObject($array);
257
    }
258
259
    /**
260
     * Escape a boolean value for safe SQL embedding.
261
     *
262
     * @param  bool  $value
263
     * @return string
264
     */
265
    protected function escapeBool($value)
266
    {
267
        return $value ? 'true' : 'false';
268
    }
269
270
    /**
271
     * Get the elapsed time since a given starting point.
272
     *
273
     * @param  int|float  $start
274
     * @return float
275
     */
276 228
    protected function getElapsedTime($start)
277
    {
278 228
        return round((microtime(true) - $start) * 1000, 2);
279
    }
280
}
281