CouchbaseConnection::execute()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 2
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
8
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
9
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
10
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
11
 * THE SOFTWARE.
12
 */
13
14
namespace Ytake\LaravelCouchbase\Database;
15
16
use Closure;
17
use Couchbase\Bucket;
18
use Couchbase\Cluster;
19
use Couchbase\ClusterManager;
20
use Couchbase\N1qlQuery;
21
use Illuminate\Database\Connection;
22
use Ytake\LaravelCouchbase\Events\QueryPrepared;
23
use Ytake\LaravelCouchbase\Events\ResultReturning;
24
use Ytake\LaravelCouchbase\Exceptions\NotSupportedException;
25
use Ytake\LaravelCouchbase\Query\Builder as QueryBuilder;
26
use Ytake\LaravelCouchbase\Query\Grammar;
27
use Ytake\LaravelCouchbase\Query\Processor;
28
use Ytake\LaravelCouchbase\Query\View;
29
use Ytake\LaravelCouchbase\Schema\Builder;
30
31
/**
32
 * Class CouchbaseConnection.
33
 *
34
 * @author Yuuki Takezawa<[email protected]>
35
 */
36
class CouchbaseConnection extends Connection
37
{
38
    /** @var string */
39
    protected $bucket;
40
41
    /** @var Cluster */
42
    protected $connection;
43
44
    /** @var */
45
    protected $managerUser;
46
47
    /** @var */
48
    protected $managerPassword;
49
50
    /** @var array */
51
    protected $options = [];
52
53
    /** @var int */
54
    protected $fetchMode = 0;
55
56
    /** @var array */
57
    protected $enableN1qlServers = [];
58
59
    /** @var string */
60
    protected $bucketPassword = '';
61
62
    /** @var string[] */
63
    protected $metrics;
64
65
    /** @var int  default consistency */
66
    protected $consistency = N1qlQuery::NOT_BOUNDED;
67
68
    /** @var string[]  function to handle the retrieval of various properties. */
69
    private $properties = [
70
        'operationTimeout',
71
        'viewTimeout',
72
        'durabilityInterval',
73
        'durabilityTimeout',
74
        'httpTimeout',
75
        'configTimeout',
76
        'configDelay',
77
        'configNodeTimeout',
78
        'htconfigIdleTimeout',
79
    ];
80
81
    /** @var array */
82
    protected $config = [];
83
84
    /** @var string */
85
    private $name;
86
87
    /** @var bool */
88
    private $crossBucket = true;
89
90
    /**
91
     * @param array  $config
92
     * @param string $name
93
     */
94 92
    public function __construct(array $config, $name)
95
    {
96 92
        $this->config = $config;
97 92
        $this->name = $name;
98 92
        $this->getManagedConfigure($config);
99
100 92
        $this->useDefaultQueryGrammar();
101
102 92
        $this->useDefaultPostProcessor();
103 92
    }
104
105
    /**
106
     * @param string $password
107
     *
108
     * @return CouchbaseConnection
109
     */
110
    public function setBucketPassword(string $password): CouchbaseConnection
111
    {
112
        $this->bucketPassword = $password;
113
114
        return $this;
115
    }
116
117
    /**
118
     * @param string $name
119
     *
120
     * @return Bucket
121
     */
122 40
    public function openBucket(string $name): Bucket
123
    {
124 40
        $couchbase = $this->getCouchbase();
125 40
        if ($this->bucketPassword === '') {
126 40
            return $couchbase->openBucket($name);
127
        }
128
129
        return $couchbase->openBucket($name, $this->bucketPassword);
130
    }
131
132
    /**
133
     * @return ClusterManager
134
     */
135 12
    public function manager(): ClusterManager
136
    {
137 12
        return $this->getCouchbase()->manager($this->managerUser, $this->managerPassword);
138
    }
139
140
    /**
141
     * @param Bucket $bucket
142
     *
143
     * @return string[]
144
     */
145 2
    public function getOptions(Bucket $bucket): array
146
    {
147 2
        $options = [];
148 2
        foreach ($this->properties as $property) {
149 2
            $options[$property] = $bucket->$property;
150
        }
151
152 2
        return $options;
153
    }
154
155
    /**
156
     * @param Bucket $bucket
157
     */
158 16
    protected function registerOption(Bucket $bucket)
159
    {
160 16
        if (count($this->options)) {
161
            foreach ($this->options as $option => $value) {
162
                $bucket->$option = $value;
163
            }
164
        }
165 16
    }
166
167
    /**
168
     * @return Processor
169
     */
170 92
    protected function getDefaultPostProcessor()
171
    {
172 92
        return new Processor();
173
    }
174
175
    /**
176
     * @return Grammar
177
     */
178 92
    protected function getDefaultQueryGrammar()
179
    {
180 92
        return new Grammar();
181
    }
182
183
    /**
184
     * @return Builder|\Illuminate\Database\Schema\Builder
185
     */
186 16
    public function getSchemaBuilder()
187
    {
188 16
        return new Builder($this);
189
    }
190
191
    /**
192
     * @param array $config enable(array), options(array), administrator(array), bucket_password(string)
193
     */
194 92
    protected function getManagedConfigure(array $config)
195
    {
196 92
        $this->enableN1qlServers = (isset($config['enables'])) ? $config['enables'] : [];
197 92
        $this->options = (isset($config['options'])) ? $config['options'] : [];
198 92
        $manager = (isset($config['administrator'])) ? $config['administrator'] : null;
199 92
        $this->managerUser = '';
200 92
        $this->managerPassword = '';
201 92
        if (!is_null($manager)) {
202 92
            $this->managerUser = $config['administrator']['user'];
203 92
            $this->managerPassword = $config['administrator']['password'];
204
        }
205 92
        $this->bucketPassword = (isset($config['bucket_password'])) ? $config['bucket_password'] : '';
206 92
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 18
    public function getName()
212
    {
213 18
        return $this->name;
214
    }
215
216
    /**
217
     * @return \Couchbase\Cluster
218
     */
219
    protected function createConnection(): Cluster
220
    {
221 74
        $this->setReconnector(function () {
222
            $this->connection = (new CouchbaseConnector)->connect($this->config);
223
224
            return $this;
225 74
        });
226
227 74
        return (new CouchbaseConnector)->connect($this->config);
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233
    public function getDriverName()
234
    {
235
        return 'couchbase';
236
    }
237
238
    /**
239
     * @return Cluster
240
     */
241 72
    public function getCouchbase(): Cluster
242
    {
243 72
        if (is_null($this->connection)) {
244 60
            $this->connection = $this->createConnection();
245
        }
246
247 72
        return $this->connection;
248
    }
249
250
    /**
251
     * @param string $table
252
     *
253
     * @return QueryBuilder
254
     */
255 16
    public function table($table)
256
    {
257 16
        return $this->bucket($table)->query()->from($table);
258
    }
259
260
    /**
261
     * @param int      $consistency
262
     * @param callable $callback
263
     *
264
     * @return mixed
265
     */
266 2
    public function callableConsistency(int $consistency, callable $callback)
267
    {
268 2
        $clone = clone $this;
269 2
        $clone->consistency = $consistency;
270
271 2
        return call_user_func_array($callback, [$clone]);
272
    }
273
274
    /**
275
     * @param int $consistency
276
     *
277
     * @return CouchbaseConnection
278
     */
279
    public function consistency(int $consistency): CouchbaseConnection
280
    {
281
        $this->consistency = $consistency;
282
283
        return $this;
284
    }
285
286
    /**
287
     * @param bool $cross
288
     */
289
    public function crossBucket(bool $cross): void
290
    {
291
        $this->crossBucket = $cross;
292
    }
293
294
    /**
295
     * @param string $bucket
296
     *
297
     * @return $this
298
     */
299 18
    public function bucket(string $bucket): CouchbaseConnection
300
    {
301 18
        $this->bucket = $bucket;
302
303 18
        return $this;
304
    }
305
306
    /**
307
     * @param N1qlQuery $query
308
     *
309
     * @return mixed
310
     */
311 16
    protected function executeQuery(N1qlQuery $query)
312
    {
313 16
        $bucket = $this->openBucket($this->bucket);
314 16
        $this->registerOption($bucket);
315 16
        $this->firePreparedQuery($query);
316 16
        $result = $bucket->query($query);
317 16
        $this->fireReturning($result);
318
319 16
        return $result;
320
    }
321
322
    /**
323
     * @param string $query
324
     * @param array  $bindings
325
     *
326
     * @return \stdClass
327
     */
328 10
    protected function execute(string $query, array $bindings = [])
329
    {
330 10
        $query = N1qlQuery::fromString($query);
331 10
        $query->consistency($this->consistency);
332 10
        $query->crossBucket($this->crossBucket);
333 10
        $query->positionalParams($bindings);
334 10
        $result = $this->executeQuery($query);
335 10
        $this->metrics = $result->metrics ?? [];
336
337 10
        return $result;
338
    }
339
340
    /**
341
     * {@inheritdoc}
342
     */
343
    public function select($query, $bindings = [], $useReadPdo = true)
344
    {
345 10
        return $this->run($query, $bindings, function ($query, $bindings) {
346 10
            if ($this->pretending()) {
347
                return [];
348
            }
349
350 10
            $result = $this->execute($query, $bindings);
351 10
            $returning = [];
352 10
            if (isset($result->rows)) {
353 10
                foreach ($result->rows as $row) {
354 4
                    if (!isset($row->{$this->bucket})) {
355 2
                        return [$row];
356
                    }
357 4
                    $returning[] = $row;
358
                }
359
            }
360
361 10
            return $returning;
362 10
        });
363
    }
364
365
    /**
366
     * {@inheritdoc}
367
     */
368
    public function cursor($query, $bindings = [], $useReadPdo = true)
369
    {
370 2
        return $this->run($query, $bindings, function ($query, $bindings) {
371
            if ($this->pretending()) {
372
                return [];
373
            }
374
375
            $result = $this->execute($query, $bindings);
376
            if (isset($result->rows)) {
377
                foreach ($result->rows as $row) {
378
                    yield $row->{$this->bucket};
379
                }
380
            }
381 2
        });
382
    }
383
384
    /**
385
     * @param string $query
386
     * @param array  $bindings
387
     *
388
     * @return int|mixed
389
     */
390 10
    public function insert($query, $bindings = [])
391
    {
392 10
        return $this->affectingStatement($query, $bindings);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->affectingS...ent($query, $bindings); (integer) is incompatible with the return type declared by the interface Illuminate\Database\ConnectionInterface::insert of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398
    public function affectingStatement($query, $bindings = [])
399
    {
400 12
        return $this->run($query, $bindings, function ($query, $bindings) {
401 12
            if ($this->pretending()) {
402
                return 0;
403
            }
404 12
            $query = N1qlQuery::fromString($query);
405 12
            $query->consistency($this->consistency);
406 12
            $query->crossBucket($this->crossBucket);
407 12
            $query->namedParams(['parameters' => $bindings]);
408 12
            $result = $this->executeQuery($query);
409 12
            $this->metrics = $result->metrics ?? [];
410 12
            if (!count($result->rows)) {
411
                return false;
412
            }
413
414 12
            return $result->rows[0]->{$this->bucket} ?? $result->rows[0];
415 12
        });
416
    }
417
418
    /**
419
     * @param string $query
420
     * @param array  $bindings
421
     *
422
     * @return mixed
423
     */
424
    public function positionalStatement(string $query, array $bindings = [])
425
    {
426 6
        return $this->run($query, $bindings, function ($query, $bindings) {
427 6
            if ($this->pretending()) {
428
                return 0;
429
            }
430 6
            $query = N1qlQuery::fromString($query);
431 6
            $query->consistency($this->consistency);
432 6
            $query->crossBucket($this->crossBucket);
433 6
            $query->positionalParams($bindings);
434 6
            $result = $this->executeQuery($query);
435 6
            $this->metrics = $result->metrics ?? [];
436 6
            if (!count($result->rows)) {
437
                return false;
438
            }
439
440 6
            return $result->rows[0]->{$this->bucket} ?? $result->rows[0];
441 6
        });
442
    }
443
444
    /**
445
     * {@inheritdoc}
446
     */
447 2
    public function transaction(Closure $callback, $attempts = 1)
448
    {
449 2
        throw new NotSupportedException(__METHOD__);
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455 2
    public function beginTransaction()
456
    {
457 2
        throw new NotSupportedException(__METHOD__);
458
    }
459
460
    /**
461
     * {@inheritdoc}
462
     */
463 2
    public function commit()
464
    {
465 2
        throw new NotSupportedException(__METHOD__);
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471 2
    public function rollBack($toLevel = null)
472
    {
473 2
        throw new NotSupportedException(__METHOD__);
474
    }
475
476
    /**
477
     * {@inheritdoc}
478
     */
479 16
    protected function reconnectIfMissingConnection()
480
    {
481 16
        if (is_null($this->connection)) {
482 12
            $this->reconnect();
483
        }
484 16
    }
485
486
    /**
487
     * {@inheritdoc}
488
     */
489 14
    public function disconnect()
490
    {
491 14
        $this->connection = null;
492 14
    }
493
494
    /**
495
     * N1QL upsert query.
496
     *
497
     * @param string $query
498
     * @param array  $bindings
499
     *
500
     * @return int
501
     */
502 2
    public function upsert(string $query, array $bindings = [])
503
    {
504 2
        return $this->affectingStatement($query, $bindings);
505
    }
506
507
    /**
508
     * Get a new query builder instance.
509
     *
510
     * @return QueryBuilder
511
     */
512 16
    public function query()
513
    {
514 16
        return new QueryBuilder(
515 16
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
516
        );
517
    }
518
519
    /**
520
     * @param string|null $bucket
521
     *
522
     * @return View
523
     */
524 2
    public function view(string $bucket = null): View
525
    {
526 2
        $bucket = is_null($bucket) ? $this->bucket : $bucket;
527
528 2
        return new View($this->openBucket($bucket), $this->events);
529
    }
530
531
    /**
532
     * Run an update statement against the database.
533
     *
534
     * @param string $query
535
     * @param array  $bindings
536
     *
537
     * @return int|\stdClass
538
     */
539 2
    public function update($query, $bindings = [])
540
    {
541 2
        return $this->positionalStatement($query, $bindings);
542
    }
543
544
    /**
545
     * Run a delete statement against the database.
546
     *
547
     * @param string $query
548
     * @param array  $bindings
549
     *
550
     * @return int|\stdClass
551
     */
552 4
    public function delete($query, $bindings = [])
553
    {
554 4
        return $this->positionalStatement($query, $bindings);
555
    }
556
557
    /**
558
     * @return \string[]
559
     */
560 2
    public function metrics(): array
561
    {
562 2
        return $this->metrics;
563
    }
564
565
    /**
566
     * @param N1qlQuery $queryObject
567
     */
568 16
    protected function firePreparedQuery(N1qlQuery $queryObject)
569
    {
570 16
        if (isset($this->events)) {
571 16
            $this->events->dispatch(new QueryPrepared($queryObject));
572
        }
573 16
    }
574
575
    /**
576
     * @param mixed $returning
577
     */
578 16
    protected function fireReturning($returning)
579
    {
580 16
        if (isset($this->events)) {
581 16
            $this->events->dispatch(new ResultReturning($returning));
582
        }
583 16
    }
584
585
    /**
586
     * @param null|\PDO $pdo
587
     *
588
     * @return $this
589
     */
590 14
    public function setPdo($pdo)
591
    {
592 14
        $this->connection = $this->createConnection();
593 14
        $this->getManagedConfigure($this->config);
594 14
        $this->useDefaultQueryGrammar();
595 14
        $this->useDefaultPostProcessor();
596
597 14
        return $this;
598
    }
599
}
600