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) { |
|
|
|
|
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>]) |
|
|
|
|
55
|
|
View Code Duplication |
if (isset($server[0])) { |
|
|
|
|
56
|
|
|
$host = (string)$server[0]; |
57
|
|
|
$port = isset($server[1]) ? (int)$server[1] : $port; |
58
|
|
|
} |
59
|
|
|
// array('host' => <host>[, 'port' => <port>]) |
|
|
|
|
60
|
|
View Code Duplication |
if (!isset($server[0]) && isset($server['host'])) { |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
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
|
|
|
/* |
|
|
|
|
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)) { |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
317
|
|
|
// don't add servers twice |
318
|
|
|
$servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']); |
|
|
|
|
319
|
|
|
if ($servers) { |
|
|
|
|
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']) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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
|
|
|
|
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 thecomposer.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
orrequire-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 you have not tested against this specific condition, such errors might go unnoticed.