Completed
Push — master ( 07905b...790544 )
by yuuki
10s
created

CouchbaseConnection   C

Complexity

Total Complexity 62

Size/Duplication

Total Lines 514
Duplicated Lines 5.84 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 89.86%

Importance

Changes 0
Metric Value
dl 30
loc 514
ccs 133
cts 148
cp 0.8986
rs 5.9493
c 0
b 0
f 0
wmc 62
lcom 2
cbo 11

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A setBucketPassword() 0 6 1
A manager() 0 4 1
A getCouchbase() 0 8 2
A beginTransaction() 0 4 1
A commit() 0 4 1
A rollBack() 0 4 1
A openBucket() 0 4 1
A registerOption() 0 8 3
A getDefaultPostProcessor() 0 4 1
A getDefaultQueryGrammar() 0 4 1
B getManagedConfigure() 0 13 6
A getName() 0 4 1
A createConnection() 0 10 1
A getDriverName() 0 4 1
A table() 0 4 1
A consistency() 0 6 1
A bucket() 0 6 1
A executeQuery() 0 10 1
A select() 0 15 4
A enableN1ql() 0 9 2
A query() 0 6 1
A update() 0 4 1
A firePreparedQuery() 0 6 2
A fireReturning() 0 6 2
A getOptions() 0 9 2
A getSchemaBuilder() 0 4 1
A callableConsistency() 0 7 1
A insert() 0 4 1
A affectingStatement() 15 15 4
A positionalStatement() 15 15 4
A transaction() 0 4 1
A reconnectIfMissingConnection() 0 6 2
A disconnect() 0 4 1
A upsert() 0 4 1
A view() 0 6 2
A delete() 0 4 1
A metrics() 0 4 1
A setPdo() 0 8 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CouchbaseConnection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CouchbaseConnection, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10
 * THE SOFTWARE.
11
 */
12
13
namespace Ytake\LaravelCouchbase\Database;
14
15
use Closure;
16
use CouchbaseBucket;
17
use Illuminate\Database\Connection;
18
use Ytake\LaravelCouchbase\Query\View;
19
use Ytake\LaravelCouchbase\Schema\Builder;
20
use Ytake\LaravelCouchbase\Query\Grammar;
21
use Ytake\LaravelCouchbase\Query\Processor;
22
use Ytake\LaravelCouchbase\Events\QueryPrepared;
23
use Ytake\LaravelCouchbase\Events\ResultReturning;
24
use Ytake\LaravelCouchbase\Query\Builder as QueryBuilder;
25
use Ytake\LaravelCouchbase\Exceptions\NotSupportedException;
26
27
/**
28
 * Class CouchbaseConnection.
29
 *
30
 * @author Yuuki Takezawa<[email protected]>
31
 */
32
class CouchbaseConnection extends Connection
33
{
34
    /** @var string */
35
    protected $bucket;
36
37
    /** @var \CouchbaseCluster */
38
    protected $connection;
39
40
    /** @var */
41
    protected $managerUser;
42
43
    /** @var */
44
    protected $managerPassword;
45
46
    /** @var array */
47
    protected $options = [];
48
49
    /** @var int */
50
    protected $fetchMode = 0;
51
52
    /** @var array */
53
    protected $enableN1qlServers = [];
54
55
    /** @var string */
56
    protected $bucketPassword = '';
57
58
    /** @var string[] */
59
    protected $metrics;
60
61
    /** @var int  default consistency */
62
    protected $consistency = \CouchbaseN1qlQuery::NOT_BOUNDED;
63
64
    /** @var string[]  function to handle the retrieval of various properties. */
65
    private $properties = [
66
        'operationTimeout',
67
        'viewTimeout',
68
        'durabilityInterval',
69
        'durabilityTimeout',
70
        'httpTimeout',
71
        'configTimeout',
72
        'configDelay',
73
        'configNodeTimeout',
74
        'htconfigIdleTimeout',
75
    ];
76
77
    /** @var array */
78
    protected $config = [];
79
80
    /** @var string */
81
    protected $name;
82
83
    /**
84
     * @param array  $config
85
     * @param string $name
86
     */
87 44
    public function __construct(array $config, $name)
88
    {
89 44
        $this->config = $config;
90 44
        $this->name = $name;
91 44
        $this->getManagedConfigure($config);
92
93 44
        $this->useDefaultQueryGrammar();
94
95 44
        $this->useDefaultPostProcessor();
96 44
    }
97
98
    /**
99
     * @param $password
100
     *
101
     * @return $this
102
     */
103
    public function setBucketPassword($password)
104
    {
105
        $this->bucketPassword = $password;
106
107
        return $this;
108
    }
109
110
    /**
111
     * @param string $name
112
     *
113
     * @return \CouchbaseBucket
114
     *
115
     * @throws \CouchbaseException
116
     */
117 19
    public function openBucket($name)
118
    {
119 19
        return $this->getCouchbase()->openBucket($name, $this->bucketPassword);
120
    }
121
122
    /**
123
     * @return \CouchbaseClusterManager
124
     */
125 2
    public function manager()
126
    {
127 2
        return $this->getCouchbase()->manager($this->managerUser, $this->managerPassword);
128
    }
129
130
    /**
131
     * @param CouchbaseBucket $bucket
132
     *
133
     * @return string[]
134
     */
135 1
    public function getOptions(\CouchbaseBucket $bucket)
136
    {
137 1
        $options = [];
138 1
        foreach ($this->properties as $property) {
139 1
            $options[$property] = $bucket->$property;
140
        }
141
142 1
        return $options;
143
    }
144
145
    /**
146
     * @param CouchbaseBucket $bucket
147
     */
148 9
    protected function registerOption(\CouchbaseBucket $bucket)
149
    {
150 9
        if (count($this->options)) {
151
            foreach ($this->options as $option => $value) {
152
                $bucket->$option = $value;
153
            }
154
        }
155 9
    }
156
157
    /**
158
     * @return Processor
159
     */
160 44
    protected function getDefaultPostProcessor()
161
    {
162 44
        return new Processor();
163
    }
164
165
    /**
166
     * @return Grammar
167
     */
168 44
    protected function getDefaultQueryGrammar()
169
    {
170 44
        return new Grammar();
171
    }
172
173
    /**
174
     * @return Builder|\Illuminate\Database\Schema\Builder
175
     */
176 6
    public function getSchemaBuilder()
177
    {
178 6
        return new Builder($this);
179
    }
180
181
    /**
182
     *
183
     * @param array $config enable(array), options(array), administrator(array), bucket_password(string)
184
     */
185 44
    protected function getManagedConfigure(array $config)
186
    {
187 44
        $this->enableN1qlServers = (isset($config['enables'])) ? $config['enables'] : [];
188 44
        $this->options = (isset($config['options'])) ? $config['options'] : [];
189 44
        $manager = (isset($config['administrator'])) ? $config['administrator'] : null;
190 44
        $this->managerUser = '';
191 44
        $this->managerPassword = '';
192 44
        if (!is_null($manager)) {
193 44
            $this->managerUser = $config['administrator']['user'];
194 44
            $this->managerPassword = $config['administrator']['password'];
195
        }
196 44
        $this->bucketPassword = (isset($config['bucket_password'])) ? $config['bucket_password'] : '';
197 44
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 10
    public function getName()
203
    {
204 10
        return $this->name;
205
    }
206
207
    /**
208
     * @return \CouchbaseCluster
209
     */
210 35
    protected function createConnection()
211
    {
212
        $this->setReconnector(function () {
213
            $this->connection = (new CouchbaseConnector)->connect($this->config);
214
215
            return $this;
216 35
        });
217
218 35
        return (new CouchbaseConnector)->connect($this->config);
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function getDriverName()
225
    {
226
        return 'couchbase';
227
    }
228
229
    /**
230
     * @return \CouchbaseCluster
231
     */
232 34
    public function getCouchbase()
233
    {
234 34
        if (is_null($this->connection)) {
235 28
            $this->connection = $this->createConnection();
236
        }
237
238 34
        return $this->connection;
239
    }
240
241
    /**
242
     * @param string $table
243
     *
244
     * @return QueryBuilder
245
     */
246 9
    public function table($table)
247
    {
248 9
        return $this->bucket($table)->query()->from($table);
249
    }
250
251
    /**
252
     * @param int      $consistency
253
     * @param callable $callback
254
     *
255
     * @return mixed
256
     */
257 1
    public function callableConsistency($consistency, callable $callback)
258
    {
259 1
        $clone = clone $this;
260 1
        $clone->consistency = $consistency;
261
262 1
        return call_user_func_array($callback, [$clone]);
263
    }
264
265
    /**
266
     * @param int $consistency
267
     *
268
     * @return $this
269
     */
270
    public function consistency($consistency)
271
    {
272
        $this->consistency = $consistency;
273
274
        return $this;
275
    }
276
277
    /**
278
     * @param string $bucket
279
     *
280
     * @return $this
281
     */
282 10
    public function bucket($bucket)
283
    {
284 10
        $this->bucket = $bucket;
285
286 10
        return $this;
287
    }
288
289
    /**
290
     * @param \CouchbaseN1qlQuery $query
291
     *
292
     * @return mixed
293
     */
294 9
    protected function executeQuery(\CouchbaseN1qlQuery $query)
295
    {
296 9
        $bucket = $this->openBucket($this->bucket);
297 9
        $this->registerOption($bucket);
298 9
        $this->firePreparedQuery($query);
299 9
        $result = $bucket->query($query);
300 9
        $this->fireReturning($result);
301
302 9
        return $result;
303
    }
304
305
    /**
306
     * {@inheritdoc}
307
     */
308 5
    public function select($query, $bindings = [], $useReadPdo = true)
309
    {
310
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
311 5
            if ($me->pretending()) {
312
                return [];
313
            }
314 5
            $query = \CouchbaseN1qlQuery::fromString($query);
315 5
            $query->consistency($this->consistency);
316 5
            $query->positionalParams($bindings);
317 5
            $result = $this->executeQuery($query);
318 5
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
319
320 5
            return (isset($result->rows)) ? $result->rows : [];
321 5
        });
322
    }
323
324
    /**
325
     * @param string $query
326
     * @param array  $bindings
327
     *
328
     * @return int|mixed
329
     */
330 5
    public function insert($query, $bindings = [])
331
    {
332 5
        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...
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338 6 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...
339
    {
340
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
341 6
            if ($me->pretending()) {
342
                return 0;
343
            }
344 6
            $query = \CouchbaseN1qlQuery::fromString($query);
345 6
            $query->consistency($this->consistency);
346 6
            $query->namedParams(['parameters' => $bindings]);
347 6
            $result = $this->executeQuery($query);
348 6
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
349
350 6
            return (isset($result->rows[0])) ? $result->rows[0] : false;
351 6
        });
352
    }
353
354
    /**
355
     * @param       $query
356
     * @param array $bindings
357
     *
358
     * @return mixed
359
     */
360 View Code Duplication
    public function positionalStatement($query, array $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...
361
    {
362 3
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
363 3
            if ($me->pretending()) {
364
                return 0;
365
            }
366 3
            $query = \CouchbaseN1qlQuery::fromString($query);
367 3
            $query->consistency($this->consistency);
368 3
            $query->positionalParams($bindings);
369 3
            $result = $this->executeQuery($query);
370 3
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
371
372 3
            return (isset($result->rows[0])) ? $result->rows[0] : false;
373 3
        });
374
    }
375
376
    /**
377
     * {@inheritdoc}
378
     */
379 1
    public function transaction(Closure $callback)
380
    {
381 1
        throw new NotSupportedException(__METHOD__);
382
    }
383
384
    /**
385
     * {@inheritdoc}
386
     */
387 1
    public function beginTransaction()
388
    {
389 1
        throw new NotSupportedException(__METHOD__);
390
    }
391
392
    /**
393
     * {@inheritdoc}
394
     */
395 1
    public function commit()
396
    {
397 1
        throw new NotSupportedException(__METHOD__);
398
    }
399
400
    /**
401
     * {@inheritdoc}
402
     */
403 1
    public function rollBack()
404
    {
405 1
        throw new NotSupportedException(__METHOD__);
406
    }
407
408
    /**
409
     * {@inheritdoc}
410
     */
411 9
    protected function reconnectIfMissingConnection()
412
    {
413 9
        if (is_null($this->connection)) {
414 6
            $this->reconnect();
415
        }
416 9
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421 7
    public function disconnect()
422
    {
423 7
        $this->connection = null;
424 7
    }
425
426
    /**
427
     * @param CouchbaseBucket $bucket
428
     *
429
     * @return CouchbaseBucket
430
     */
431
    protected function enableN1ql(CouchbaseBucket $bucket)
432
    {
433
        if (!count($this->enableN1qlServers)) {
434
            return $bucket;
435
        }
436
        $bucket->enableN1ql($this->enableN1qlServers);
437
438
        return $bucket;
439
    }
440
441
    /**
442
     * N1QL upsert query.
443
     *
444
     * @param string $query
445
     * @param array  $bindings
446
     *
447
     * @return int
448
     */
449 1
    public function upsert($query, $bindings = [])
450
    {
451 1
        return $this->affectingStatement($query, $bindings);
452
    }
453
454
    /**
455
     * Get a new query builder instance.
456
     *
457
     * @return QueryBuilder
458
     */
459 9
    public function query()
460
    {
461 9
        return new QueryBuilder(
462 9
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
463
        );
464
    }
465
466
    /**
467
     * @param string|null $bucket
468
     *
469
     * @return View
470
     */
471 1
    public function view($bucket = null)
472
    {
473 1
        $bucket = is_null($bucket) ? $this->bucket : $bucket;
474
475 1
        return new View($this->openBucket($bucket), $this->events);
476
    }
477
478
    /**
479
     * Run an update statement against the database.
480
     *
481
     * @param string $query
482
     * @param array  $bindings
483
     *
484
     * @return int|\stdClass
485
     */
486 1
    public function update($query, $bindings = [])
487
    {
488 1
        return $this->positionalStatement($query, $bindings);
489
    }
490
491
    /**
492
     * Run a delete statement against the database.
493
     *
494
     * @param string $query
495
     * @param array  $bindings
496
     *
497
     * @return int|\stdClass
498
     */
499 2
    public function delete($query, $bindings = [])
500
    {
501 2
        return $this->positionalStatement($query, $bindings);
502
    }
503
504
    /**
505
     * @return \string[]
506
     */
507 1
    public function metrics()
508
    {
509 1
        return $this->metrics;
510
    }
511
512
    /**
513
     * @param \CouchbaseN1qlQuery $queryObject
514
     */
515 9
    protected function firePreparedQuery(\CouchbaseN1qlQuery $queryObject)
516
    {
517 9
        if (isset($this->events)) {
518 9
            $this->events->fire(new QueryPrepared($queryObject));
519
        }
520 9
    }
521
522
    /**
523
     * @param mixed $returning
524
     */
525 9
    protected function fireReturning($returning)
526
    {
527 9
        if (isset($this->events)) {
528 9
            $this->events->fire(new ResultReturning($returning));
529
        }
530 9
    }
531
532
    /**
533
     * @param null|\PDO $pdo
534
     *
535
     * @return $this
536
     */
537 7
    public function setPdo($pdo)
538
    {
539 7
        $this->connection = $this->createConnection($this->config);
0 ignored issues
show
Unused Code introduced by
The call to CouchbaseConnection::createConnection() has too many arguments starting with $this->config.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
540 7
        $this->getManagedConfigure($this->config);
541 7
        $this->useDefaultQueryGrammar();
542 7
        $this->useDefaultPostProcessor();
543 7
        return $this;
544
    }
545
}
546