Test Failed
Pull Request — master (#4)
by
unknown
11:18
created

Connection::select()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 1
nop 3
dl 0
loc 36
rs 8.5806
c 0
b 0
f 0
ccs 7
cts 7
cp 1
crap 4
1
<?php
2
/**
3
 * @author Donii Sergii <[email protected]>
4
 */
5
6
namespace sonrac\Arango;
7
8
use ArangoDBClient\CollectionHandler as ArangoDBCollectionHandler;
9
use ArangoDBClient\Connection as ArangoDBConnection;
10
use ArangoDBClient\ConnectionOptions as ArangoDBConnectionOptions;
11
use ArangoDBClient\Document;
12
use ArangoDBClient\Exception as ArangoException;
13
use ArangoDBClient\Exception;
14
use ArangoDBClient\Statement;
15
use ArangoDBClient\UpdatePolicy as ArangoDBUpdatePolicy;
16
use Illuminate\Database\Connection as IlluminateConnection;
17
use sonrac\Arango\Query\Grammars\Grammar;
18
use sonrac\Arango\Query\Processors\Processor;
19
use sonrac\Arango\Query\QueryBuilder;
20
21
/**
22
 * Class Connection.
23
 * Arango connection.
24
 *
25
 * @author  Donii Sergii <[email protected]>
26
 */
27
class Connection extends IlluminateConnection
28
{
29
    /**
30
     * Arango.DB Query Builder
31
     *
32
     * @var
33
     *
34
     * @author Donii Sergii <[email protected]>
35
     */
36
    protected $db;
37
38
    protected $database = '_system';
39
40
    /**
41
     * Arango.DB connection
42
     *
43
     * @var \ArangoDBClient\Connection
44
     *
45
     * @author Donii Sergii <[email protected]>
46
     */
47
    protected $arangoConnection;
48
49
    /**
50 11
     * Connection constructor.
51
     *
52 11
     * @param array $config Connection options
53
     *
54 11
     * @throws Exception
55
     *
56 11
     * @author Donii Sergii <[email protected]>
57 11
     */
58
    public function __construct(array $config = [])
59
    {
60
        $this->config = $config;
61
62
        $this->arangoConnection = $this->createConnection();
63
64
        $this->db = new ArangoDBCollectionHandler($this->arangoConnection);
65
66 1
        // We need to initialize a query grammar and the query post processors
67
        // which are both very important parts of the database abstractions
68 1
        // so we initialize these to their default values while starting.
69
        $this->useDefaultQueryGrammar();
70
71
        $this->useDefaultPostProcessor();
72
    }
73
74
    /**
75
     * Send AQL request to ArangoDB and return response with flat array
76
     *
77
     * @param string $query
78 1
     * @param array $bindings
79
     * @param bool $useReadPdo
80 1
     * @return mixed
81
     */
82
    public function select($query, $bindings = [], $useReadPdo = true)
83
    {
84
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
85
            if ($this->pretending()) {
86
                return [];
87
            }
88
89
            $query = $this->prepareBindingsInQuery($query);
90
            $options = [
91
                'query' => $query,
92 11
                'count' => true,
93
                'batchSize' => 1000,
94 11
                'sanitize'  => true
95
            ];
96 11
97
            if(count($bindings) > 0){
98 11
                $options['bindVars'] = $this->prepareBindings($bindings);
99
                var_dump($options['bindVars']);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($options['bindVars']); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
100
            }
101
102
            $statement = new Statement($this->getArangoClient(), $options);
103
104
            $cursor = $statement->execute();
105
106
            $resultingDocuments = [];
107
108 11
            foreach ($cursor as $key => $document) {
0 ignored issues
show
Bug introduced by
The expression $cursor of type object<ArangoDBClient\Cursor>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
109
                /**
110 11
                 * @var Document $document
111
                 */
112 11
                $resultingDocuments[$key] = $document->getAll();
113
            }
114
115
            return $resultingDocuments;
116
        });
117
    }
118
    /**
119
     * @inheritdoc
120
     */
121
    public function query()
122 11
    {
123
        return new QueryBuilder(
124 11
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
125
        );
126
    }
127
128
    /**
129
     * @inheritdoc
130
     */
131 View Code Duplication
    public function statement($query, $bindings = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
    {
133
        return $this->run($query, $bindings, function ($query, $bindings) {
134 11
            if ($this->pretending()) {
135
                return true;
136 11
            }
137
138
            $query = $this->prepareBindingsInQuery($query);
139
            $options = [
140
                'query' => $query,
141
                'count' => true,
142
                'batchSize' => 1000,
143
                'sanitize'  => true
144
            ];
145
146 11
            if(count($bindings) > 0){
147
                $options['bindVars'] = $this->prepareBindings($bindings);
148 11
            }
149
150
            $statement = new Statement($this->getArangoClient(), $options);
151
152
            return $statement->execute();
153
        });
154
    }
155
156
    /**
157
     * @inheritdoc
158 11
     */
159 View Code Duplication
    public function affectingStatement($query, $bindings = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160 11
    {
161
        return $this->run($query, $bindings, function ($query, $bindings) {
162
            $query = $this->prepareBindingsInQuery($query);
163
164
            $options = [
165
                'query' => $query,
166
                'count' => true,
167
                'batchSize' => 1000,
168
                'sanitize'  => true
169
            ];
170 11
171
            if(count($bindings) > 0){
172 11
                $options['bindVars'] = $this->prepareBindings($bindings);
173
            }
174
175
            $statement = new Statement($this->getArangoClient(), $options);
176
177
            return $statement->execute();
178
        });
179
    }
180
181
    /**
182 11
     * Get Arango.DB
183
     *
184 11
     * @return mixed
185
     *
186
     * @author Donii Sergii <[email protected]>
187
     */
188
    public function getArangoDB()
189
    {
190
        return $this->db;
191
    }
192
193
    /**
194 11
     * Get Arango.DB client
195
     *
196 11
     * @return \ArangoDBClient\Connection
197
     *
198
     * @author Donii Sergii <[email protected]>
199
     */
200
    public function getArangoClient()
201
    {
202
        return $this->arangoConnection;
203
    }
204
205
    /**
206 11
     * Create new arango.db connection.
207
     *
208 11
     * @param array $config Config
209
     *
210
     * @return ArangoDBConnection
211
     *
212
     * @throws \ArangoDBClient\Exception
213
     *
214
     * @author Donii Sergii <[email protected]>
215
     */
216
    public function createConnection(array $config = [])
217
    {
218 11
        $config = $this->config + $config;
219
220 11
        ArangoException::enableLogging();
221
222
        return new ArangoDBConnection($this->getMainConnectionOption() + $config);
223
    }
224
225
    /**
226
     * Get database name.
227
     *
228
     * @return string
229
     *
230
     * @author Donii Sergii <[email protected]>
231
     */
232
    public function getDatabaseName()
233 11
    {
234
        $this->database = $this->getOption(ArangoDBConnectionOptions::OPTION_DATABASE, $this->database);
235 11
236 11
        return parent::getDatabaseName();
237
    }
238
239 11
    /**
240
     * Get arango.db endpoint.
241
     *
242
     * @return string
243
     *
244
     * @author Donii Sergii <[email protected]>
245
     */
246
    public function getEndPoint()
247
    {
248
        return $this->getOption(ArangoDBConnectionOptions::OPTION_ENDPOINT, 'tcp://127.0.0.1:8529');
249 11
    }
250
251
    /**
252
     * Get auth type
253 11
     *
254
     * @return string
255 11
     *
256
     * @author Donii Sergii <[email protected]>
257 11
     */
258
    public function getAuthType()
259 11
    {
260
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_TYPE, 'Basic');
261 11
    }
262
263 11
    /**
264
     * Get auth type
265 11
     *
266
     * @return string
267 11
     *
268
     * @author Donii Sergii <[email protected]>
269 11
     */
270
    public function getAuthUser()
271 11
    {
272
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_USER, 'root');
273
    }
274
275
    /**
276
     * Get auth type
277
     *
278
     * @return string
279
     *
280
     * @author Donii Sergii <[email protected]>
281
     */
282
    public function getAuthPassword()
283
    {
284
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_PASSWD, '');
285
    }
286
287
    /**
288
     * Get connection option
289
     *
290
     * @return string
291
     *
292
     * @author Donii Sergii <[email protected]>
293
     */
294
    public function getConnectionOption()
295
    {
296
        return $this->getOption(ArangoDBConnectionOptions::OPTION_CONNECTION, 'Keep-Alive');
297
    }
298
299
    /**
300
     * Get timeout option
301
     *
302
     * @return int
303
     *
304
     * @author Donii Sergii <[email protected]>
305
     */
306
    public function getTimeout()
307
    {
308
        return $this->getOption(ArangoDBConnectionOptions::OPTION_TIMEOUT, 3);
309
    }
310
311
    /**
312
     * Get reconnect option
313
     *
314
     * @return bool
315
     *
316
     * @author Donii Sergii <[email protected]>
317
     */
318
    public function getReconnect()
319
    {
320
        return $this->getOption(ArangoDBConnectionOptions::OPTION_RECONNECT, true);
321
    }
322
323
    /**
324
     * Get create option
325
     *
326
     * @return mixed
327
     *
328
     * @author Donii Sergii <[email protected]>
329
     */
330
    public function getCreate()
331
    {
332
        return $this->getOption(ArangoDBConnectionOptions::OPTION_CREATE, true);
333
    }
334
335
    /**
336
     * Get update policy option
337
     *
338
     * @return string
339
     *
340
     * @author Donii Sergii <[email protected]>
341
     */
342
    public function getUpdatePolicy()
343
    {
344
        return $this->getOption(ArangoDBConnectionOptions::OPTION_UPDATE_POLICY, ArangoDBUpdatePolicy::LAST);
345
    }
346
347
    public function getDefaultQueryGrammar()
348
    {
349
        return new Grammar();
350
    }
351
352
    public function getDefaultPostProcessor()
353
    {
354
        return new Processor();
355
    }
356
357
    protected function prepareBindingsInQuery($query){
358
        $query = explode('?', $query);
359
        $result = "";
360
        foreach ($query as $index => $part){
361
            if($index === count($query) - 1){
362
                $result .= $part;
363
                continue;
364
            }
365
            $result .= $part.'@B'.($index+1);
366
        }
367
        return $result;
368
    }
369
370
    /**
371
     * Reconnect to the database if a PDO connection is missing.
372
     *
373
     * @throws \ArangoDBClient\Exception
374
     * @return void
375
     */
376
    protected function reconnectIfMissingConnection()
377
    {
378
        if (is_null($this->arangoConnection)) {
379
            $this->arangoConnection = $this->createConnection();
380
        }
381
    }
382
383
    /**
384
     * Get option value
385
     *
386
     * @param string $optionName
387
     * @param mixed  $default
388
     *
389
     * @return mixed
390
     *
391
     * @author Donii Sergii <[email protected]>
392
     */
393
    private function getOption($optionName, $default)
394
    {
395
        if (!isset($this->config[$optionName])) {
396
            $this->config[$optionName] = $default;
397
        }
398
399
        return $this->config[$optionName];
400
    }
401
402
    /**
403
     * Get main connection option
404
     *
405
     * @return array
406
     *
407
     * @author Donii Sergii <[email protected]>
408
     */
409
    private function getMainConnectionOption()
410
    {
411
        return [
412
            // database name
413
            ArangoDBConnectionOptions::OPTION_DATABASE      => $this->getDatabaseName(),
414
            // server endpoint to connect to
415
            ArangoDBConnectionOptions::OPTION_ENDPOINT      => $this->getEndPoint(),
416
            // authorization type to use (currently supported: 'Basic')
417
            ArangoDBConnectionOptions::OPTION_AUTH_TYPE     => $this->getAuthType(),
418
            // user for basic authorization
419
            ArangoDBConnectionOptions::OPTION_AUTH_USER     => $this->getAuthUser(),
420
            // password for basic authorization
421
            ArangoDBConnectionOptions::OPTION_AUTH_PASSWD   => $this->getAuthPassword(),
422
            // connection persistence on server. can use either 'Close' (one-time connections) or 'Keep-Alive' (re-used connections)
423
            ArangoDBConnectionOptions::OPTION_CONNECTION    => $this->getConnectionOption(),
424
            // connect timeout in seconds
425
            ArangoDBConnectionOptions::OPTION_TIMEOUT       => $this->getTimeout(),
426
            // whether or not to reconnect when a keep-alive connection has timed out on server
427
            ArangoDBConnectionOptions::OPTION_RECONNECT     => $this->getReconnect(),
428
            // optionally create new collections when inserting documents
429
            ArangoDBConnectionOptions::OPTION_CREATE        => $this->getCreate(),
430
            // optionally create new collections when inserting documents
431
            ArangoDBConnectionOptions::OPTION_UPDATE_POLICY => $this->getUpdatePolicy(),
432
        ];
433
    }
434
}
435