Passed
Push — refactor/improve-static-analys... ( bb4224...d678be )
by
unknown
07:03
created

Connection::getSchemaBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1.0156

Importance

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

215
            'array' => $this->escapeArray(/** @scrutinizer ignore-type */ $value),
Loading history...
216
            'boolean' => $this->escapeBool($value),
217
            'double' => (string) $value,
218
            'integer' => (string) $value,
219
            'NULL' => 'null',
220
            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

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