CouchbaseResourceManager   C
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 495
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 34.55%

Importance

Changes 28
Bugs 9 Features 13
Metric Value
wmc 59
c 28
b 9
f 13
lcom 1
cbo 3
dl 0
loc 495
ccs 66
cts 191
cp 0.3455
rs 6.1905

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getServer() 0 12 3
C normalizeServer() 0 44 13
A hasResource() 0 4 1
A getResource() 0 22 3
B setResource() 0 26 4
A removeResource() 0 6 1
A setServer() 0 13 2
B addServers() 0 25 4
A addServer() 0 4 1
A setUsername() 0 13 2
A getUsername() 0 10 2
A setEncoder() 0 13 2
A getEncoder() 0 10 2
A setDecoder() 0 13 2
A getDecoder() 0 10 2
A setBucket() 0 13 2
A getBucket() 0 10 2
A setPassword() 0 13 2
A getPassword() 0 10 2
A normalizeServers() 0 13 4
A compareServers() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like CouchbaseResourceManager 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 CouchbaseResourceManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * zf-couchbase2.
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 THE
11
 * SOFTWARE.
12
 *
13
 * @copyright 2015 MehrAlsNix (http://www.mehralsnix.de)
14
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
15
 *
16
 * @link      http://github.com/MehrAlsNix/zf-couchbase2
17
 */
18
namespace MehrAlsNix\ZF\Cache\Storage\Adapter;
19
20
use CouchbaseBucket as CouchbaseBucketResource;
21
use CouchbaseCluster as CouchbaseClusterResource;
22
use Zend\Cache\Exception;
23
use Zend\Stdlib\ArrayUtils;
24
25
/**
26
 * Class CouchbaseResourceManager.
27
 */
28
class CouchbaseResourceManager
0 ignored issues
show
Complexity introduced by
This class has a complexity of 59 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

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

Loading history...
29
{
30
    /**
31
     * Registered resources.
32
     *
33
     * @var array
34
     */
35
    protected $resources = [];
36
37
    /**
38
     * Get servers.
39
     *
40
     * @param string $id
41
     *
42
     * @throws Exception\RuntimeException
43
     *
44
     * @return array array('host' => <host>, 'port' => <port>)
45
     */
46
    public function getServer($id)
47
    {
48
        if (!$this->hasResource($id)) {
49
            throw new Exception\RuntimeException("No resource with id '{$id}'");
50
        }
51
        $resource = &$this->resources[$id];
52
        if ($resource instanceof CouchbaseBucketResource) {
53
            return $resource->manager()->info()['hostname'];
54
        }
55
56
        return $resource['server'];
57
    }
58
59
    /**
60
     * Normalize one server into the following format:
61
     * array('host' => <host>, 'port' => <port>, 'weight' => <weight>).
62
     *
63
     * @param string|array &$server
64
     *
65
     * @throws Exception\InvalidArgumentException
66
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
67
     */
68 61
    protected function normalizeServer(&$server)
0 ignored issues
show
Complexity introduced by
This operation has 328 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
69
    {
70 61
        $host = null;
71 61
        $port = 8091;
72
        // convert a single server into an array
73 61
        if ($server instanceof \Traversable) {
74
            $server = ArrayUtils::iteratorToArray($server);
75
        }
76 61
        if (is_array($server)) {
77
            // array(<host>[, <port>])
78 61
            if (isset($server[0])) {
79 61
                $host = (string) $server[0];
80 61
                $port = isset($server[1]) ? (int) $server[1] : $port;
81 61
            }
82
            // array('host' => <host>[, 'port' => <port>])
83 61
            if (!isset($server[0]) && isset($server['host'])) {
84
                $host = (string) $server['host'];
85
                $port = isset($server['port']) ? (int) $server['port'] : $port;
86
            }
87 61
        } else {
88
            // parse server from URI host{:?port}
89
            $server = trim($server);
90
            if (strpos($server, '://') === false) {
91
                $server = 'couchbase://'.$server;
92
            }
93
            $server = parse_url($server);
94
            if (!$server) {
95
                throw new Exception\InvalidArgumentException('Invalid server given');
96
            }
97
            $host = $server['host'];
98
            $port = isset($server['port']) ? (int) $server['port'] : $port;
99
            if (isset($server['query'])) {
100
                $query = null;
101
                parse_str($server['query'], $query);
102
            }
103
        }
104 61
        if (!$host) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $host of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
105
            throw new Exception\InvalidArgumentException('Missing required server host');
106
        }
107
        $server = [
108 61
            'host' => $host,
109 61
            'port' => $port,
110 61
        ];
111 61
    }
112
113
    /**
114
     * Check if a resource exists.
115
     *
116
     * @param string $id
117
     *
118
     * @return bool
119
     */
120 61
    public function hasResource($id)
121
    {
122 61
        return isset($this->resources[$id]);
123
    }
124
125
    /**
126
     * Gets a couchbase cluster resource.
127
     *
128
     * @param string $id
129
     *
130
     * @return \CouchbaseBucket
131
     *
132
     * @throws Exception\RuntimeException
133
     */
134 61
    public function getResource($id)
135
    {
136 61
        if (!$this->hasResource($id)) {
137
            throw new Exception\RuntimeException("No resource with id '{$id}'");
138
        }
139 61
        $resource = $this->resources[$id];
140 61
        if ($resource instanceof \CouchbaseBucket) {
141 61
            return $resource;
142
        }
143 61
        $memc = new CouchbaseClusterResource('couchbase://'.$resource['server'][0]['host'].':'.$resource['server'][0]['port'], (string) $resource['username'], (string) $resource['password']);
144 61
        $bucket = $memc->openBucket((string) $resource['bucket'], (string) $resource['password']);
145
146 61
        $bucket->setTranscoder(
147 61
            $resource['encoder'],
148 61
            $resource['decoder']
149 61
        );
150
151
        // buffer and return
152 61
        $this->resources[$id] = $bucket;
153
154 61
        return $bucket;
155
    }
156
157
    /**
158
     * Set a resource.
159
     *
160
     * @param string                                     $id
161
     * @param array|\Traversable|CouchbaseBucketResource $resource
162
     *
163
     * @return CouchbaseResourceManager Fluent interface
164
     *
165
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
166
     * @throws Exception\InvalidArgumentException
167
     */
168 61
    public function setResource($id, $resource)
169
    {
170 61
        $id = (string) $id;
171 61
        if (!($resource instanceof CouchbaseBucketResource)) {
172 61
            if ($resource instanceof \Traversable) {
173
                $resource = ArrayUtils::iteratorToArray($resource);
174 61
            } elseif (!is_array($resource)) {
175
                throw new Exception\InvalidArgumentException(
176
                    'Resource must be an instance of CouchbaseCluster or an array or Traversable'
177
                );
178
            }
179 61
            $resource = array_merge([
180 61
                'server' => '',
181 61
                'username' => '',
182 61
                'password' => '',
183 61
                'bucket' => '',
184 61
                'encoder' => 'couchbase_default_encoder',
185
                'decoder' => 'couchbase_default_decoder'
186 61
            ], $resource);
187
            // normalize and validate params
188 61
            $this->normalizeServers($resource['server']);
189 61
        }
190 61
        $this->resources[$id] = $resource;
191
192 61
        return $this;
193
    }
194
195
    /**
196
     * Remove a resource.
197
     *
198
     * @param string $id
199
     *
200
     * @return CouchbaseResourceManager Fluent interface
201
     */
202
    public function removeResource($id)
203
    {
204
        unset($this->resources[$id]);
205
206
        return $this;
207
    }
208
209
    /**
210
     * Set servers.
211
     *
212
     * $servers can be an array list or a comma separated list of servers.
213
     * One server in the list can be descripted as follows:
214
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
215
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
216
     * - List:  array(<host>[, <port>][, <weight>])
217
     *
218
     * @param string $id
219
     * @param string $server
220
     *
221
     * @return CouchbaseResourceManager
222
     */
223 61
    public function setServer($id, $server)
224
    {
225 61
        if (!$this->hasResource($id)) {
226 61
            return $this->setResource($id, [
227 61
                'server' => $server,
228 61
            ]);
229
        }
230
        $this->normalizeServers($server);
231
        $resource = &$this->resources[$id];
232
        $resource['server'] = $server;
233
234
        return $this;
235
    }
236
237
    /**
238
     * Add servers.
239
     *
240
     * @param string       $id
241
     * @param string|array $servers
242
     *
243
     * @return CouchbaseResourceManager
244
     */
245
    public function addServers($id, $servers)
246
    {
247
        if (!$this->hasResource($id)) {
248
            return $this->setResource($id, [
249
                'servers' => $servers,
250
            ]);
251
        }
252
        $this->normalizeServers($servers);
253
        $resource = &$this->resources[$id];
254
        if ($resource instanceof CouchbaseBucketResource) {
255
            // don't add servers twice
256
            $servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']);
0 ignored issues
show
Bug introduced by
It seems like $servers can also be of type string; however, array_udiff() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
257
            if ($servers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $servers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
258
                $resource->addServers($servers);
259
            }
260
        } else {
261
            // don't add servers twice
262
            $resource['servers'] = array_merge(
263
                $resource['servers'],
264
                array_udiff($servers, $resource['servers'], [$this, 'compareServers'])
0 ignored issues
show
Bug introduced by
It seems like $servers defined by parameter $servers on line 245 can also be of type string; however, array_udiff() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
265
            );
266
        }
267
268
        return $this;
269
    }
270
271
    /**
272
     * Add one server.
273
     *
274
     * @param string       $id
275
     * @param string|array $server
276
     *
277
     * @return CouchbaseResourceManager
278
     */
279
    public function addServer($id, $server)
280
    {
281
        return $this->addServers($id, [$server]);
282
    }
283
284
    /**
285
     * Set servers.
286
     *
287
     * $servers can be an array list or a comma separated list of servers.
288
     * One server in the list can be descripted as follows:
289
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
290
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
291
     * - List:  array(<host>[, <port>][, <weight>])
292
     *
293
     * @param string $id
294
     * @param string $username
295
     *
296
     * @return CouchbaseResourceManager
297
     */
298
    public function setUsername($id, $username)
299
    {
300
        if (!$this->hasResource($id)) {
301
            return $this->setResource($id, [
302
                'username' => $username,
303
            ]);
304
        }
305
306
        $resource = &$this->resources[$id];
307
        $resource['username'] = $username;
308
309
        return $this;
310
    }
311
312
    public function getUsername($id)
313
    {
314
        if (!$this->hasResource($id)) {
315
            return $this->getResource($id);
316
        }
317
318
        $resource = &$this->resources[$id];
319
320
        return $resource['username'];
321
    }
322
323
    /**
324
     * Set encoder.
325
     *
326
     * @param string $id
327
     * @param string $encoder
328
     *
329
     * @return CouchbaseResourceManager
330
     */
331
    public function setEncoder($id, $encoder)
332
    {
333
        if (!$this->hasResource($id)) {
334
            return $this->setResource($id, [
335
                'encoder' => $encoder,
336
            ]);
337
        }
338
339
        $resource = &$this->resources[$id];
340
        $resource['encoder'] = $encoder;
341
342
        return $this;
343
    }
344
345
    /**
346
     * Get encoder.
347
     *
348
     * @param $id
349
     *
350
     * @return CouchbaseBucketResource
351
     */
352
    public function getEncoder($id)
353
    {
354
        if (!$this->hasResource($id)) {
355
            return $this->getResource($id);
356
        }
357
358
        $resource = &$this->resources[$id];
359
360
        return $resource['encoder'];
361
    }
362
363
    /**
364
     * Set decoder.
365
     *
366
     * @param string $id
367
     * @param string $encoder
368
     *
369
     * @return CouchbaseResourceManager
370
     */
371
    public function setDecoder($id, $encoder)
372
    {
373
        if (!$this->hasResource($id)) {
374
            return $this->setResource($id, [
375
                'encoder' => $encoder,
376
            ]);
377
        }
378
379
        $resource = &$this->resources[$id];
380
        $resource['encoder'] = $encoder;
381
382
        return $this;
383
    }
384
385
    /**
386
     * Get decoder.
387
     *
388
     * @param $id
389
     *
390
     * @return CouchbaseBucketResource
391
     */
392
    public function getDecoder($id)
393
    {
394
        if (!$this->hasResource($id)) {
395
            return $this->getResource($id);
396
        }
397
398
        $resource = &$this->resources[$id];
399
400
        return $resource['encoder'];
401
    }
402
403
    /**
404
     * Set servers.
405
     *
406
     * $servers can be an array list or a comma separated list of servers.
407
     * One server in the list can be descripted as follows:
408
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
409
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
410
     * - List:  array(<host>[, <port>][, <weight>])
411
     *
412
     * @param string $id
413
     * @param string $bucket
414
     *
415
     * @return CouchbaseResourceManager
416
     */
417 61
    public function setBucket($id, $bucket)
418
    {
419 61
        if (!$this->hasResource($id)) {
420
            return $this->setResource($id, [
421
                'bucket' => $bucket,
422
            ]);
423
        }
424
425 61
        $resource = &$this->resources[$id];
426 61
        $resource['bucket'] = $bucket;
427
428 61
        return $this;
429
    }
430
431
    public function getBucket($id)
432
    {
433
        if (!$this->hasResource($id)) {
434
            return $this->getResource($id);
435
        }
436
437
        $resource = &$this->resources[$id];
438
439
        return $resource['bucket'];
440
    }
441
442
    /**
443
     * Set servers.
444
     *
445
     * $servers can be an array list or a comma separated list of servers.
446
     * One server in the list can be descripted as follows:
447
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
448
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
449
     * - List:  array(<host>[, <port>][, <weight>])
450
     *
451
     * @param string $id
452
     * @param string $password
453
     *
454
     * @return CouchbaseResourceManager
455
     */
456
    public function setPassword($id, $password)
457
    {
458
        if (!$this->hasResource($id)) {
459
            return $this->setResource($id, [
460
                'password' => $password,
461
            ]);
462
        }
463
464
        $resource = &$this->resources[$id];
465
        $resource['password'] = $password;
466
467
        return $this;
468
    }
469
470
    public function getPassword($id)
471
    {
472
        if (!$this->hasResource($id)) {
473
            return $this->getResource($id);
474
        }
475
476
        $resource = &$this->resources[$id];
477
478
        return $resource['password'];
479
    }
480
481
    /**
482
     * Normalize a list of servers into the following format:
483
     * array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...]).
484
     *
485
     * @param string|array $servers
486
     *
487
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
488
     */
489 61
    protected function normalizeServers(&$servers)
490
    {
491 61
        if (!is_array($servers) && !$servers instanceof \Traversable) {
492
            // Convert string into a list of servers
493
            $servers = explode(',', $servers);
494
        }
495 61
        $result = [];
496 61
        foreach ($servers as $server) {
497 61
            $this->normalizeServer($server);
498 61
            $result[$server['host'].':'.$server['port']] = $server;
499 61
        }
500 61
        $servers = array_values($result);
501 61
    }
502
503
    /**
504
     * Compare 2 normalized server arrays
505
     * (Compares only the host and the port).
506
     *
507
     * @param array $serverA
508
     * @param array $serverB
509
     *
510
     * @return int
511
     */
512
    protected function compareServers(array $serverA, array $serverB)
513
    {
514
        $keyA = $serverA['host'].':'.$serverA['port'];
515
        $keyB = $serverB['host'].':'.$serverB['port'];
516
        if ($keyA === $keyB) {
517
            return 0;
518
        }
519
520
        return $keyA > $keyB ? 1 : -1;
521
    }
522
}
523