Completed
Push — develop ( 6133cf...8c2664 )
by Siad
03:31
created

CouchbaseResourceManager::encoder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
namespace MehrAlsNix\ZF\Cache\Storage\Adapter;
4
5
use CouchbaseBucket as CouchbaseBucketResource;
6
use CouchbaseCluster as CouchbaseClusterResource;
7
use Zend\Cache\Exception;
8
use Zend\Stdlib\ArrayUtils;
9
10
class CouchbaseResourceManager
11
{
12
    /**
13
     * Registered resources
14
     *
15
     * @var array
16
     */
17
    protected $resources = [];
18
19
    /**
20
     * Get servers
21
     * @param string $id
22
     * @throws Exception\RuntimeException
23
     * @return array array('host' => <host>, 'port' => <port>)
24
     */
25
    public function getServer($id)
26
    {
27
        if (!$this->hasResource($id)) {
28
            throw new Exception\RuntimeException("No resource with id '{$id}'");
29
        }
30
        $resource = &$this->resources[$id];
31
        if ($resource instanceof CouchbaseBucketResource) {
1 ignored issue
show
Bug introduced by
The class CouchbaseBucket does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
32
            return $resource->manager()->info()['hostname'];
33
        }
34
        return $resource['server'];
35
    }
36
37
    /**
38
     * Normalize one server into the following format:
39
     * array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
40
     *
41
     * @param string|array &$server
42
     * @throws Exception\InvalidArgumentException
43
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
44
     */
45
    protected function normalizeServer(&$server)
46
    {
47
        $host = null;
48
        $port = 8091;
49
        // convert a single server into an array
50
        if ($server instanceof \Traversable) {
51
            $server = ArrayUtils::iteratorToArray($server);
52
        }
53
        if (is_array($server)) {
54
            // array(<host>[, <port>])
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
55 View Code Duplication
            if (isset($server[0])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
56
                $host = (string)$server[0];
57
                $port = isset($server[1]) ? (int)$server[1] : $port;
58
            }
59
            // array('host' => <host>[, 'port' => <port>])
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
60 View Code Duplication
            if (!isset($server[0]) && isset($server['host'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
61
                $host = (string)$server['host'];
62
                $port = isset($server['port']) ? (int)$server['port'] : $port;
63
            }
64
        } else {
65
            // parse server from URI host{:?port}
66
            $server = trim($server);
67
            if (strpos($server, '://') === false) {
68
                $server = 'http://' . $server;
69
            }
70
            $server = parse_url($server);
71
            if (!$server) {
72
                throw new Exception\InvalidArgumentException('Invalid server given');
73
            }
74
            $host = $server['host'];
75
            $port = isset($server['port']) ? (int)$server['port'] : $port;
76
            if (isset($server['query'])) {
77
                $query = null;
78
                parse_str($server['query'], $query);
79
            }
80
        }
81
        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...
82
            throw new Exception\InvalidArgumentException('Missing required server host');
83
        }
84
        $server = [
85
            'host' => $host,
86
            'port' => $port
87
        ];
88
    }
89
90
    /**
91
     * Check if a resource exists
92
     *
93
     * @param string $id
94
     * @return bool
95
     */
96
    public function hasResource($id)
97
    {
98
        return isset($this->resources[$id]);
99
    }
100
101
    /**
102
     * Gets a couchbase cluster resource
103
     *
104
     * @param string $id
105
     * @return \CouchbaseBucket
106
     * @throws Exception\RuntimeException
107
     */
108
    public function getResource($id)
109
    {
110
        if (!$this->hasResource($id)) {
111
            throw new Exception\RuntimeException("No resource with id '{$id}'");
112
        }
113
        $resource = $this->resources[$id];
114
        if ($resource instanceof \CouchbaseBucket) {
1 ignored issue
show
Bug introduced by
The class CouchbaseBucket does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
115
            return $resource;
116
        }
117
        $memc = new CouchbaseClusterResource('couchbase://' . $resource['server'][0]['host'] . ':' . $resource['server'][0]['port'], (string)$resource['username'], (string)$resource['password']);
118
        $bucket = $memc->openBucket((string)$resource['bucket'], (string)$resource['password']);
119
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
120
        $bucket->setTranscoder(
121
            '\MehrAlsNix\ZF\Cache\Storage\Adapter\CouchbaseResourceManager::encoder',
122
            '\MehrAlsNix\ZF\Cache\Storage\Adapter\CouchbaseResourceManager::decoder'
123
        );
124
        */
125
126
        // buffer and return
127
        $this->resources[$id] = $bucket;
128
        return $bucket;
129
    }
130
131
    /**
132
     * Set a resource
133
     *
134
     * @param string $id
135
     * @param array|\Traversable|CouchbaseBucketResource $resource
136
     * @return CouchbaseResourceManager Fluent interface
137
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
138
     * @throws Exception\InvalidArgumentException
139
     */
140
    public function setResource($id, $resource)
141
    {
142
        $id = (string)$id;
143
        if (!($resource instanceof CouchbaseBucketResource)) {
1 ignored issue
show
Bug introduced by
The class CouchbaseBucket does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
144
            if ($resource instanceof \Traversable) {
145
                $resource = ArrayUtils::iteratorToArray($resource);
146
            } elseif (!is_array($resource)) {
147
                throw new Exception\InvalidArgumentException(
148
                    'Resource must be an instance of CouchbaseCluster or an array or Traversable'
149
                );
150
            }
151
            $resource = array_merge([
152
                'lib_options' => [],
153
                'server' => '',
154
                'username' => '',
155
                'password' => '',
156
                'bucket' => ''
157
            ], $resource);
158
            // normalize and validate params
159
            $this->normalizeServers($resource['server']);
160
            $this->normalizeLibOptions($resource['lib_options']);
161
        }
162
        $this->resources[$id] = $resource;
163
        return $this;
164
    }
165
166
    /**
167
     * Normalize libmemcached options
168
     *
169
     * @param array|\Traversable $libOptions
170
     * @throws Exception\InvalidArgumentException
171
     */
172
    protected function normalizeLibOptions(& $libOptions)
173
    {
174
        if (!is_array($libOptions) && !($libOptions instanceof \Traversable)) {
175
            throw new Exception\InvalidArgumentException(
176
                'Lib-Options must be an array or an instance of Traversable'
177
            );
178
        }
179
180
        $result = [];
181
        foreach ($libOptions as $key => $value) {
182
            $this->normalizeLibOptionKey($key);
183
            $result[$key] = $value;
184
        }
185
186
        $libOptions = $result;
187
    }
188
189
    /**
190
     * Convert option name into it's constant value
191
     *
192
     * @param string|int $key
193
     * @throws Exception\InvalidArgumentException
194
     */
195
    protected function normalizeLibOptionKey(& $key)
196
    {
197
        // convert option name into it's constant value
198
        if (is_string($key)) {
199
            $const = '\\COUCHBASE_SERTYPE_' . str_replace([' ', '-'], '_', strtoupper($key));
200
            if (!defined($const)) {
201
                throw new Exception\InvalidArgumentException("Unknown libcouchbase option '{$key}' ({$const})");
202
            }
203
            $key = constant($const);
204
        } else {
205
            $key = (int) $key;
206
        }
207
    }
208
209
    /**
210
     * Set Libmemcached options
211
     *
212
     * @param string $id
213
     * @param array  $libOptions
214
     * @return CouchbaseResourceManager Fluent interface
215
     */
216
    public function setLibOptions($id, array $libOptions)
217
    {
218
        if (!$this->hasResource($id)) {
219
            return $this->setResource($id, [
220
                'lib_options' => $libOptions
221
            ]);
222
        }
223
224
        $this->normalizeLibOptions($libOptions);
225
226
        $resource = & $this->resources[$id];
227
        $resource['lib_options'] = $libOptions;
228
229
        return $this;
230
    }
231
232
    /**
233
     * Get Libmemcached options
234
     *
235
     * @param string $id
236
     * @return array
237
     * @throws Exception\RuntimeException
238
     */
239
    public function getLibOptions($id)
240
    {
241
        if (!$this->hasResource($id)) {
242
            throw new Exception\RuntimeException("No resource with id '{$id}'");
243
        }
244
245
        $resource = & $this->resources[$id];
246
247
        if ($resource instanceof MemcachedResource) {
0 ignored issues
show
Bug introduced by
The class MehrAlsNix\ZF\Cache\Stor...apter\MemcachedResource does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
248
            $libOptions = [];
249
            $reflection = new ReflectionClass('Memcached');
250
            $constants  = $reflection->getConstants();
251
            foreach ($constants as $constName => $constValue) {
252
                if (substr($constName, 0, 4) == 'OPT_') {
253
                    $libOptions[$constValue] = $resource->getOption($constValue);
254
                }
255
            }
256
            return $libOptions;
257
        }
258
        return $resource['lib_options'];
259
    }
260
261
    /**
262
     * Remove a resource
263
     *
264
     * @param string $id
265
     * @return CouchbaseResourceManager Fluent interface
266
     */
267
    public function removeResource($id)
268
    {
269
        unset($this->resources[$id]);
270
        return $this;
271
    }
272
273
    /**
274
     * Set servers
275
     *
276
     * $servers can be an array list or a comma separated list of servers.
277
     * One server in the list can be descripted as follows:
278
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
279
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
280
     * - List:  array(<host>[, <port>][, <weight>])
281
     *
282
     * @param string $id
283
     * @param string $server
284
     * @return CouchbaseResourceManager
285
     */
286
    public function setServer($id, $server)
287
    {
288
        if (!$this->hasResource($id)) {
289
            return $this->setResource($id, [
290
                'server' => $server
291
            ]);
292
        }
293
        $this->normalizeServers($server);
294
        $resource = &$this->resources[$id];
295
        $resource['server'] = $server;
296
297
        return $this;
298
    }
299
300
    /**
301
     * Add servers
302
     *
303
     * @param string $id
304
     * @param string|array $servers
305
     * @return CouchbaseResourceManager
306
     */
307
    public function addServers($id, $servers)
308
    {
309
        if (!$this->hasResource($id)) {
310
            return $this->setResource($id, [
311
                'servers' => $servers
312
            ]);
313
        }
314
        $this->normalizeServers($servers);
315
        $resource = &$this->resources[$id];
316
        if ($resource instanceof CouchbaseBucketResource) {
1 ignored issue
show
Bug introduced by
The class CouchbaseBucket does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
317
            // don't add servers twice
318
            $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...
319
            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...
320
                $resource->addServers($servers);
321
            }
322
        } else {
323
            // don't add servers twice
324
            $resource['servers'] = array_merge(
325
                $resource['servers'],
326
                array_udiff($servers, $resource['servers'], [$this, 'compareServers'])
0 ignored issues
show
Bug introduced by
It seems like $servers defined by parameter $servers on line 307 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...
327
            );
328
        }
329
        return $this;
330
    }
331
332
    /**
333
     * Add one server
334
     *
335
     * @param string $id
336
     * @param string|array $server
337
     * @return CouchbaseResourceManager
338
     */
339
    public function addServer($id, $server)
340
    {
341
        return $this->addServers($id, [$server]);
342
    }
343
344
    /**
345
     * Set servers
346
     *
347
     * $servers can be an array list or a comma separated list of servers.
348
     * One server in the list can be descripted as follows:
349
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
350
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
351
     * - List:  array(<host>[, <port>][, <weight>])
352
     *
353
     * @param string $id
354
     * @param string $username
355
     * @return CouchbaseResourceManager
356
     */
357 View Code Duplication
    public function setUsername($id, $username)
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...
358
    {
359
        if (!$this->hasResource($id)) {
360
            return $this->setResource($id, [
361
                'username' => $username
362
            ]);
363
        }
364
365
        $resource = &$this->resources[$id];
366
        $resource['username'] = $username;
367
368
        return $this;
369
    }
370
371 View Code Duplication
    public function getUsername($id)
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...
372
    {
373
        if (!$this->hasResource($id)) {
374
            return $this->getResource($id);
375
        }
376
377
        $resource = &$this->resources[$id];
378
        return $resource['username'];
379
    }
380
381
    /**
382
     * Set servers
383
     *
384
     * $servers can be an array list or a comma separated list of servers.
385
     * One server in the list can be descripted as follows:
386
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
387
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
388
     * - List:  array(<host>[, <port>][, <weight>])
389
     *
390
     * @param string $id
391
     * @param string $bucket
392
     * @return CouchbaseResourceManager
393
     */
394
    public function setBucket($id, $bucket)
395
    {
396
        if (!$this->hasResource($id)) {
397
            return $this->setResource($id, [
398
                'bucket' => $bucket
399
            ]);
400
        }
401
402
        $resource = &$this->resources[$id];
403
        $resource['bucket'] = $bucket;
404
405
        return $this;
406
    }
407
408 View Code Duplication
    public function getBucket($id)
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...
409
    {
410
        if (!$this->hasResource($id)) {
411
            return $this->getResource($id);
412
        }
413
414
        $resource = &$this->resources[$id];
415
        return $resource['bucket'];
416
    }
417
418
    /**
419
     * Set servers
420
     *
421
     * $servers can be an array list or a comma separated list of servers.
422
     * One server in the list can be descripted as follows:
423
     * - URI:   [tcp://]<host>[:<port>][?weight=<weight>]
424
     * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
425
     * - List:  array(<host>[, <port>][, <weight>])
426
     *
427
     * @param string $id
428
     * @param string $password
429
     * @return CouchbaseResourceManager
430
     */
431
    public function setPassword($id, $password)
432
    {
433
        if (!$this->hasResource($id)) {
434
            return $this->setResource($id, [
435
                'password' => $password
436
            ]);
437
        }
438
439
        $resource = &$this->resources[$id];
440
        $resource['password'] = $password;
441
442
        return $this;
443
    }
444
445 View Code Duplication
    public function getPassword($id)
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...
446
    {
447
        if (!$this->hasResource($id)) {
448
            return $this->getResource($id);
449
        }
450
451
        $resource = &$this->resources[$id];
452
        return $resource['password'];
453
    }
454
455
    /**
456
     * Normalize a list of servers into the following format:
457
     * array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...])
458
     *
459
     * @param string|array $servers
460
     * @throws \Zend\Stdlib\Exception\InvalidArgumentException
461
     */
462
    protected function normalizeServers(& $servers)
463
    {
464
        if (!is_array($servers) && !$servers instanceof \Traversable) {
465
            // Convert string into a list of servers
466
            $servers = explode(',', $servers);
467
        }
468
        $result = [];
469
        foreach ($servers as $server) {
470
            $this->normalizeServer($server);
471
            $result[$server['host'] . ':' . $server['port']] = $server;
472
        }
473
        $servers = array_values($result);
474
    }
475
476
    /**
477
     * Compare 2 normalized server arrays
478
     * (Compares only the host and the port)
479
     *
480
     * @param array $serverA
481
     * @param array $serverB
482
     * @return int
483
     */
484
    protected function compareServers(array $serverA, array $serverB)
485
    {
486
        $keyA = $serverA['host'] . ':' . $serverA['port'];
487
        $keyB = $serverB['host'] . ':' . $serverB['port'];
488
        if ($keyA === $keyB) {
489
            return 0;
490
        }
491
        return $keyA > $keyB ? 1 : -1;
492
    }
493
}
494