1 | <?php |
||||||||
2 | |||||||||
3 | namespace MatthiasMullie\Scrapbook\Adapters; |
||||||||
4 | |||||||||
5 | use MatthiasMullie\Scrapbook\Adapters\Collections\Couchbase as Collection; |
||||||||
6 | use MatthiasMullie\Scrapbook\Exception\Exception; |
||||||||
7 | use MatthiasMullie\Scrapbook\Exception\InvalidKey; |
||||||||
8 | use MatthiasMullie\Scrapbook\Exception\ServerUnhealthy; |
||||||||
9 | use MatthiasMullie\Scrapbook\KeyValueStore; |
||||||||
10 | |||||||||
11 | /** |
||||||||
12 | * Couchbase adapter. Basically just a wrapper over \CouchbaseBucket, but in an |
||||||||
13 | * exchangeable (KeyValueStore) interface. |
||||||||
14 | * |
||||||||
15 | * @see http://developer.couchbase.com/documentation/server/4.0/sdks/php-2.0/php-intro.html |
||||||||
16 | * @see http://docs.couchbase.com/sdk-api/couchbase-php-client-2.1.0/ |
||||||||
17 | * @see http://docs.couchbase.com/sdk-api/couchbase-php-client-2.6.2/ |
||||||||
18 | * |
||||||||
19 | * @author Matthias Mullie <[email protected]> |
||||||||
20 | * @copyright Copyright (c) 2014, Matthias Mullie. All rights reserved |
||||||||
21 | * @license LICENSE MIT |
||||||||
22 | */ |
||||||||
23 | class Couchbase implements KeyValueStore |
||||||||
24 | { |
||||||||
25 | /** |
||||||||
26 | * @var \CouchbaseBucket|\Couchbase\Bucket|\Couchbase\Collection |
||||||||
0 ignored issues
–
show
|
|||||||||
27 | * \CouchbaseBucket for Couchbase SDK <=2.2, |
||||||||
28 | * \Couchbase\Bucket for SDK >=2.3 & <3.0, |
||||||||
29 | * \Couchbase\Collection for SDK >=3.0 |
||||||||
30 | */ |
||||||||
31 | protected $collection; |
||||||||
32 | |||||||||
33 | /** |
||||||||
34 | * @var \CouchbaseBucketManager|\Couchbase\BucketManager|\Couchbase\Management\BucketManager |
||||||||
0 ignored issues
–
show
The type
Couchbase\Management\BucketManager was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
35 | * \CouchbaseBucketManager for Couchbase SDK <=2.2, |
||||||||
36 | * \Couchbase\BucketManager for SDK >=2.3 & <4.0, |
||||||||
37 | * \Couchbase\Management\BucketManager for SDK >=4.0 |
||||||||
38 | */ |
||||||||
39 | protected $bucketManager; |
||||||||
40 | |||||||||
41 | /** |
||||||||
42 | * @var \CouchbaseBucket|\Couchbase\Bucket |
||||||||
43 | * \CouchbaseBucket for Couchbase SDK <=2.2, |
||||||||
44 | * \Couchbase\Bucket for SDK >=2.3 |
||||||||
45 | */ |
||||||||
46 | protected $bucket; |
||||||||
47 | |||||||||
48 | /** |
||||||||
49 | * @var int|null Timeout in ms |
||||||||
50 | */ |
||||||||
51 | protected $timeout; |
||||||||
52 | |||||||||
53 | /** |
||||||||
54 | * @param \CouchbaseBucket|\Couchbase\Bucket|\Couchbase\Collection $client |
||||||||
55 | * \CouchbaseBucket for Couchbase SDK <=2.2, |
||||||||
56 | * \Couchbase\Bucket for SDK >=2.3 & <3.0, |
||||||||
57 | * \Couchbase\Collection for SDK >=3.0 |
||||||||
58 | * @param \CouchbaseBucketManager|\Couchbase\BucketManager|\Couchbase\Management\BucketManager|false $bucketManager |
||||||||
59 | * \CouchbaseBucketManager for Couchbase SDK <=2.2, |
||||||||
60 | * \Couchbase\BucketManager for SDK >=2.3 & <4.0, |
||||||||
61 | * \Couchbase\Management\BucketManager for SDK >=4.0, |
||||||||
62 | * false for compatibility with when this 2nd argument was $assertServerHealthy |
||||||||
63 | * @param \CouchbaseBucket|\Couchbase\Bucket $bucketManager |
||||||||
64 | * \CouchbaseBucket for Couchbase SDK <=2.2, |
||||||||
65 | * \Couchbase\Bucket for SDK >=2.3, |
||||||||
66 | * null for compatibility with when this argument didn't yet exist |
||||||||
67 | * @param int $timeout K/V timeout in ms |
||||||||
68 | * |
||||||||
69 | * @throws ServerUnhealthy |
||||||||
70 | */ |
||||||||
71 | public function __construct( |
||||||||
72 | /* \CouchbaseBucket|\Couchbase\Bucket|\Couchbase\Collection */ |
||||||||
73 | $client, |
||||||||
74 | /* \CouchbaseBucketManager|\Couchbase\BucketManager|\Couchbase\Management\BucketManager|false */ |
||||||||
75 | $bucketManager, |
||||||||
76 | /* \CouchbaseBucket|\Couchbase\Bucket|null */ |
||||||||
77 | $bucket, |
||||||||
78 | /* int|null */ |
||||||||
79 | $timeout = null |
||||||||
80 | ) { |
||||||||
81 | // BC: $assertServerHealthy used to be 2nd argument |
||||||||
82 | $assertServerHealthy = is_bool($bucketManager) ? $bucketManager : false; |
||||||||
83 | $this->timeout = $timeout; |
||||||||
84 | |||||||||
85 | if ($client instanceof \CouchbaseBucket) { |
||||||||
86 | // SDK <=2.2 |
||||||||
87 | $this->collection = $client; |
||||||||
88 | $this->bucket = $bucket instanceof \CouchbaseBucket ? $bucket : $client; |
||||||||
89 | |||||||||
90 | if ($bucketManager instanceof \CouchbaseBucketManager) { |
||||||||
91 | $this->bucketManager = $bucketManager; |
||||||||
92 | } else { |
||||||||
93 | $this->bucketManager = $client->manager(); |
||||||||
94 | } |
||||||||
95 | |||||||||
96 | if ($assertServerHealthy) { |
||||||||
97 | $info = $this->bucketManager->info(); |
||||||||
98 | foreach ($info['nodes'] as $node) { |
||||||||
99 | if ('healthy' !== $node['status']) { |
||||||||
100 | throw new ServerUnhealthy('Server isn\'t ready yet'); |
||||||||
101 | } |
||||||||
102 | } |
||||||||
103 | } |
||||||||
104 | } elseif ($client instanceof \Couchbase\Bucket && !method_exists($client, 'defaultCollection')) { |
||||||||
105 | // SDK <3.0 |
||||||||
106 | $this->collection = $client; |
||||||||
107 | $this->bucket = $bucket instanceof \Couchbase\Bucket ? $bucket : $client; |
||||||||
108 | |||||||||
109 | if ($bucketManager instanceof \Couchbase\BucketManager) { |
||||||||
110 | $this->bucketManager = $bucketManager; |
||||||||
111 | } elseif (method_exists($client, 'manager')) { |
||||||||
112 | $this->bucketManager = $client->manager(); |
||||||||
113 | } |
||||||||
114 | |||||||||
115 | if ($assertServerHealthy) { |
||||||||
116 | $info = $this->bucket->ping(); |
||||||||
117 | foreach ($info['services']['kv'] as $kv) { |
||||||||
118 | if ('ok' !== $kv['state']) { |
||||||||
119 | throw new ServerUnhealthy('Server isn\'t ready yet'); |
||||||||
120 | } |
||||||||
121 | } |
||||||||
122 | } |
||||||||
123 | } elseif ( |
||||||||
124 | $client instanceof \Couchbase\Collection && $bucket instanceof \Couchbase\Bucket && |
||||||||
125 | ( |
||||||||
126 | // SDK >= 3.0 & < 4.0 |
||||||||
127 | $bucketManager instanceof \Couchbase\BucketManager || |
||||||||
128 | // SDK >= 4.0 |
||||||||
129 | $bucketManager instanceof \Couchbase\Management\BucketManager |
||||||||
130 | ) |
||||||||
131 | ) { |
||||||||
132 | $this->collection = $client; |
||||||||
133 | $this->bucketManager = $bucketManager; |
||||||||
134 | $this->bucket = $bucket; |
||||||||
135 | } elseif ( |
||||||||
136 | // received bucket for client, but since we didn't go down the SDK <3.0 |
||||||||
137 | // path, we're on a more recent SDK & should've received a collection |
||||||||
138 | $client instanceof \Couchbase\Bucket || |
||||||||
139 | // received collection, but other params are invalid |
||||||||
140 | $client instanceof \Couchbase\Collection |
||||||||
141 | ) { |
||||||||
142 | throw new Exception('Invalid Couchbase adapter constructor arguments. \Couchbase\Collection, \Couchbase\BucketManager & \Couchbase\Bucket arguments are required for Couchbase SDK >= 3.x or 4.x'); |
||||||||
143 | } else { |
||||||||
144 | throw new Exception('Invalid Couchbase adapter constructor arguments'); |
||||||||
145 | } |
||||||||
146 | } |
||||||||
147 | |||||||||
148 | /** |
||||||||
149 | * {@inheritdoc} |
||||||||
150 | */ |
||||||||
151 | public function get($key, &$token = null) |
||||||||
152 | { |
||||||||
153 | $this->assertValidKey($key); |
||||||||
154 | |||||||||
155 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
156 | // SDK >=3.0 |
||||||||
157 | try { |
||||||||
158 | $options = new \Couchbase\GetOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\GetOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
159 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
160 | $result = $this->collection->get($key, $options); |
||||||||
0 ignored issues
–
show
It seems like
$options can also be of type Couchbase\GetOptions ; however, parameter $options of Couchbase\Bucket::get() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||||
161 | $token = $result->cas(); |
||||||||
0 ignored issues
–
show
The method
cas() does not exist on Couchbase\Document .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||||
162 | |||||||||
163 | return $this->unserialize($result->content()); |
||||||||
0 ignored issues
–
show
The method
content() does not exist on Couchbase\Document .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||||
164 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
0 ignored issues
–
show
The type
Couchbase\Exception\CouchbaseException was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
165 | // SDK >=4.0 |
||||||||
166 | $token = null; |
||||||||
167 | |||||||||
168 | return false; |
||||||||
169 | } catch (\Couchbase\BaseException $e) { |
||||||||
0 ignored issues
–
show
The type
Couchbase\BaseException was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
170 | // SDK >=3.0 & <4.0 |
||||||||
171 | $token = null; |
||||||||
172 | |||||||||
173 | return false; |
||||||||
174 | } |
||||||||
175 | } |
||||||||
176 | |||||||||
177 | // SDK <3.0 |
||||||||
178 | try { |
||||||||
179 | $result = $this->collection->get($key); |
||||||||
180 | } catch (\CouchbaseException $e) { |
||||||||
181 | $token = null; |
||||||||
182 | |||||||||
183 | return false; |
||||||||
184 | } |
||||||||
185 | |||||||||
186 | $token = $result->cas; |
||||||||
187 | |||||||||
188 | return $result->error ? false : $this->unserialize($result->value); |
||||||||
189 | } |
||||||||
190 | |||||||||
191 | /** |
||||||||
192 | * {@inheritdoc} |
||||||||
193 | */ |
||||||||
194 | public function getMulti(array $keys, array &$tokens = null) |
||||||||
195 | { |
||||||||
196 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
197 | // SDK >=3.0 no longer provides *multi operations |
||||||||
198 | $results = array(); |
||||||||
199 | $tokens = array(); |
||||||||
200 | foreach ($keys as $key) { |
||||||||
201 | $token = null; |
||||||||
202 | $value = $this->get($key, $token); |
||||||||
203 | |||||||||
204 | if (null !== $token) { |
||||||||
205 | $results[$key] = $value; |
||||||||
206 | $tokens[$key] = $token; |
||||||||
207 | } |
||||||||
208 | } |
||||||||
209 | |||||||||
210 | return $results; |
||||||||
211 | } |
||||||||
212 | |||||||||
213 | // SDK <3.0 |
||||||||
214 | array_map(array($this, 'assertValidKey'), $keys); |
||||||||
215 | |||||||||
216 | $tokens = array(); |
||||||||
217 | if (empty($keys)) { |
||||||||
218 | return array(); |
||||||||
219 | } |
||||||||
220 | |||||||||
221 | try { |
||||||||
222 | $results = $this->collection->get($keys); |
||||||||
223 | } catch (\CouchbaseException $e) { |
||||||||
224 | return array(); |
||||||||
225 | } |
||||||||
226 | |||||||||
227 | $values = array(); |
||||||||
228 | $tokens = array(); |
||||||||
229 | |||||||||
230 | foreach ($results as $key => $value) { |
||||||||
231 | if (!in_array($key, $keys) || $value->error) { |
||||||||
232 | continue; |
||||||||
233 | } |
||||||||
234 | |||||||||
235 | $values[$key] = $this->unserialize($value->value); |
||||||||
236 | $tokens[$key] = $value->cas; |
||||||||
237 | } |
||||||||
238 | |||||||||
239 | return $values; |
||||||||
240 | } |
||||||||
241 | |||||||||
242 | /** |
||||||||
243 | * {@inheritdoc} |
||||||||
244 | */ |
||||||||
245 | public function set($key, $value, $expire = 0) |
||||||||
246 | { |
||||||||
247 | $this->assertValidKey($key); |
||||||||
248 | |||||||||
249 | if ($this->deleteIfExpired($key, $expire)) { |
||||||||
250 | return true; |
||||||||
251 | } |
||||||||
252 | |||||||||
253 | $value = $this->serialize($value); |
||||||||
254 | |||||||||
255 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
256 | // SDK >=3.0 |
||||||||
257 | try { |
||||||||
258 | $options = new \Couchbase\UpsertOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\UpsertOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
259 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
260 | $options = $options->expiry($expire); |
||||||||
261 | $this->collection->upsert($key, $value, $options); |
||||||||
262 | |||||||||
263 | return true; |
||||||||
264 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
265 | // SDK >=4.0 |
||||||||
266 | return false; |
||||||||
267 | } catch (\Couchbase\BaseException $e) { |
||||||||
268 | // SDK >=3.0 & <4.0 |
||||||||
269 | return false; |
||||||||
270 | } |
||||||||
271 | } |
||||||||
272 | |||||||||
273 | // SDK <3.0 |
||||||||
274 | try { |
||||||||
275 | $options = array('expiry' => $expire); |
||||||||
276 | $result = $this->collection->upsert($key, $value, $options); |
||||||||
277 | } catch (\CouchbaseException $e) { |
||||||||
278 | return false; |
||||||||
279 | } |
||||||||
280 | |||||||||
281 | return !$result->error; |
||||||||
282 | } |
||||||||
283 | |||||||||
284 | /** |
||||||||
285 | * {@inheritdoc} |
||||||||
286 | */ |
||||||||
287 | public function setMulti(array $items, $expire = 0) |
||||||||
288 | { |
||||||||
289 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
290 | // SDK >=3.0 no longer provides *multi operations |
||||||||
291 | $success = array(); |
||||||||
292 | foreach ($items as $key => $value) { |
||||||||
293 | $success[$key] = $this->set($key, $value, $expire); |
||||||||
294 | } |
||||||||
295 | |||||||||
296 | return $success; |
||||||||
297 | } |
||||||||
298 | |||||||||
299 | // SDK <3.0 |
||||||||
300 | array_map(array($this, 'assertValidKey'), array_keys($items)); |
||||||||
301 | |||||||||
302 | if (empty($items)) { |
||||||||
303 | return array(); |
||||||||
304 | } |
||||||||
305 | |||||||||
306 | $keys = array_keys($items); |
||||||||
307 | if ($this->deleteIfExpired($keys, $expire)) { |
||||||||
308 | return array_fill_keys($keys, true); |
||||||||
309 | } |
||||||||
310 | |||||||||
311 | // attempting to insert integer keys (e.g. '0' as key is automatically |
||||||||
312 | // cast to int, if it's an array key) fails with a segfault, so we'll |
||||||||
313 | // have to do those piecemeal |
||||||||
314 | $integers = array_filter(array_keys($items), 'is_int'); |
||||||||
315 | if ($integers) { |
||||||||
0 ignored issues
–
show
The expression
$integers 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
Loading history...
|
|||||||||
316 | $success = array(); |
||||||||
317 | $integers = array_intersect_key($items, array_fill_keys($integers, null)); |
||||||||
318 | foreach ($integers as $k => $v) { |
||||||||
319 | $success[$k] = $this->set((string) $k, $v, $expire); |
||||||||
320 | } |
||||||||
321 | |||||||||
322 | $items = array_diff_key($items, $integers); |
||||||||
323 | |||||||||
324 | return array_merge($success, $this->setMulti($items, $expire)); |
||||||||
325 | } |
||||||||
326 | |||||||||
327 | foreach ($items as $key => $value) { |
||||||||
328 | $items[$key] = array( |
||||||||
329 | 'value' => $this->serialize($value), |
||||||||
330 | 'expiry' => $expire, |
||||||||
331 | ); |
||||||||
332 | } |
||||||||
333 | |||||||||
334 | try { |
||||||||
335 | $results = $this->collection->upsert($items); |
||||||||
0 ignored issues
–
show
The call to
Couchbase\Bucket::upsert() has too few arguments starting with value .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has less 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. Please note the @ignore annotation hint above.
Loading history...
|
|||||||||
336 | } catch (\CouchbaseException $e) { |
||||||||
337 | return array_fill_keys(array_keys($items), false); |
||||||||
338 | } |
||||||||
339 | |||||||||
340 | $success = array(); |
||||||||
341 | foreach ($results as $key => $result) { |
||||||||
342 | $success[$key] = !$result->error; |
||||||||
343 | } |
||||||||
344 | |||||||||
345 | return $success; |
||||||||
346 | } |
||||||||
347 | |||||||||
348 | /** |
||||||||
349 | * {@inheritdoc} |
||||||||
350 | */ |
||||||||
351 | public function delete($key) |
||||||||
352 | { |
||||||||
353 | $this->assertValidKey($key); |
||||||||
354 | |||||||||
355 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
356 | // SDK >=3.0 |
||||||||
357 | try { |
||||||||
358 | $options = new \Couchbase\RemoveOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\RemoveOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
359 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
360 | $this->collection->remove($key, $options); |
||||||||
0 ignored issues
–
show
It seems like
$options can also be of type Couchbase\RemoveOptions ; however, parameter $options of Couchbase\Bucket::remove() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||||
361 | |||||||||
362 | return true; |
||||||||
363 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
364 | // SDK >=4.0 |
||||||||
365 | return false; |
||||||||
366 | } catch (\Couchbase\BaseException $e) { |
||||||||
367 | // SDK >=3.0 & <4.0 |
||||||||
368 | return false; |
||||||||
369 | } |
||||||||
370 | } |
||||||||
371 | |||||||||
372 | // SDK <3.0 |
||||||||
373 | try { |
||||||||
374 | $result = $this->collection->remove($key); |
||||||||
375 | } catch (\CouchbaseException $e) { |
||||||||
376 | return false; |
||||||||
377 | } |
||||||||
378 | |||||||||
379 | return !$result->error; |
||||||||
380 | } |
||||||||
381 | |||||||||
382 | /** |
||||||||
383 | * {@inheritdoc} |
||||||||
384 | */ |
||||||||
385 | public function deleteMulti(array $keys) |
||||||||
386 | { |
||||||||
387 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
388 | // SDK >=3.0 no longer provides *multi operations |
||||||||
389 | $success = array(); |
||||||||
390 | foreach ($keys as $key) { |
||||||||
391 | $success[$key] = $this->delete($key); |
||||||||
392 | } |
||||||||
393 | |||||||||
394 | return $success; |
||||||||
395 | } |
||||||||
396 | |||||||||
397 | // SDK <3.0 |
||||||||
398 | array_map(array($this, 'assertValidKey'), $keys); |
||||||||
399 | |||||||||
400 | if (empty($keys)) { |
||||||||
401 | return array(); |
||||||||
402 | } |
||||||||
403 | |||||||||
404 | try { |
||||||||
405 | $results = $this->collection->remove($keys); |
||||||||
406 | } catch (\CouchbaseException $e) { |
||||||||
407 | return array_fill_keys($keys, false); |
||||||||
408 | } |
||||||||
409 | |||||||||
410 | $success = array(); |
||||||||
411 | foreach ($results as $key => $result) { |
||||||||
412 | $success[$key] = !$result->error; |
||||||||
413 | } |
||||||||
414 | |||||||||
415 | return $success; |
||||||||
416 | } |
||||||||
417 | |||||||||
418 | /** |
||||||||
419 | * {@inheritdoc} |
||||||||
420 | */ |
||||||||
421 | public function add($key, $value, $expire = 0) |
||||||||
422 | { |
||||||||
423 | $this->assertValidKey($key); |
||||||||
424 | |||||||||
425 | $value = $this->serialize($value); |
||||||||
426 | |||||||||
427 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
428 | // SDK >=3.0 |
||||||||
429 | try { |
||||||||
430 | $options = new \Couchbase\InsertOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\InsertOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
431 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
432 | $options = $options->expiry($expire); |
||||||||
433 | $this->collection->insert($key, $value, $options); |
||||||||
434 | |||||||||
435 | $this->deleteIfExpired($key, $expire); |
||||||||
436 | |||||||||
437 | return true; |
||||||||
438 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
439 | // SDK >=4.0 |
||||||||
440 | return false; |
||||||||
441 | } catch (\Couchbase\BaseException $e) { |
||||||||
442 | // SDK >=3.0 & <4.0 |
||||||||
443 | return false; |
||||||||
444 | } |
||||||||
445 | } |
||||||||
446 | |||||||||
447 | // SDK <3.0 |
||||||||
448 | try { |
||||||||
449 | $options = array('expiry' => $expire); |
||||||||
450 | $result = $this->collection->insert($key, $value, $options); |
||||||||
451 | } catch (\CouchbaseException $e) { |
||||||||
452 | return false; |
||||||||
453 | } |
||||||||
454 | |||||||||
455 | $success = !$result->error; |
||||||||
456 | |||||||||
457 | // Couchbase is imprecise in its expiration handling, so we can clean up |
||||||||
458 | // stuff that is already expired (assuming the `add` succeeded) |
||||||||
459 | if ($success) { |
||||||||
460 | $this->deleteIfExpired($key, $expire); |
||||||||
461 | } |
||||||||
462 | |||||||||
463 | return $success; |
||||||||
464 | } |
||||||||
465 | |||||||||
466 | /** |
||||||||
467 | * {@inheritdoc} |
||||||||
468 | */ |
||||||||
469 | public function replace($key, $value, $expire = 0) |
||||||||
470 | { |
||||||||
471 | $this->assertValidKey($key); |
||||||||
472 | |||||||||
473 | $value = $this->serialize($value); |
||||||||
474 | |||||||||
475 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
476 | // SDK >=3.0 |
||||||||
477 | try { |
||||||||
478 | $options = new \Couchbase\ReplaceOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\ReplaceOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
479 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
480 | $options = $options->expiry($expire); |
||||||||
481 | $this->collection->replace($key, $value, $options); |
||||||||
482 | |||||||||
483 | $this->deleteIfExpired($key, $expire); |
||||||||
484 | |||||||||
485 | return true; |
||||||||
486 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
487 | // SDK >=4.0 |
||||||||
488 | return false; |
||||||||
489 | } catch (\Couchbase\BaseException $e) { |
||||||||
490 | // SDK >=3.0 & <4.0 |
||||||||
491 | return false; |
||||||||
492 | } |
||||||||
493 | } |
||||||||
494 | |||||||||
495 | // SDK <3.0 |
||||||||
496 | try { |
||||||||
497 | $options = array('expiry' => $expire); |
||||||||
498 | $result = $this->collection->replace($key, $value, $options); |
||||||||
499 | } catch (\CouchbaseException $e) { |
||||||||
500 | return false; |
||||||||
501 | } |
||||||||
502 | |||||||||
503 | $success = !$result->error; |
||||||||
504 | |||||||||
505 | // Couchbase is imprecise in its expiration handling, so we can clean up |
||||||||
506 | // stuff that is already expired (assuming the `replace` succeeded) |
||||||||
507 | if ($success) { |
||||||||
508 | $this->deleteIfExpired($key, $expire); |
||||||||
509 | } |
||||||||
510 | |||||||||
511 | return $success; |
||||||||
512 | } |
||||||||
513 | |||||||||
514 | /** |
||||||||
515 | * {@inheritdoc} |
||||||||
516 | */ |
||||||||
517 | public function cas($token, $key, $value, $expire = 0) |
||||||||
518 | { |
||||||||
519 | $this->assertValidKey($key); |
||||||||
520 | |||||||||
521 | $value = $this->serialize($value); |
||||||||
522 | |||||||||
523 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
524 | // SDK >=3.0 |
||||||||
525 | if (null === $token) { |
||||||||
526 | return false; |
||||||||
527 | } |
||||||||
528 | try { |
||||||||
529 | $options = new \Couchbase\ReplaceOptions(); |
||||||||
530 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
531 | $options = $options->expiry($expire); |
||||||||
532 | $options = $options->cas($token); |
||||||||
533 | $this->collection->replace($key, $value, $options); |
||||||||
534 | |||||||||
535 | $this->deleteIfExpired($key, $expire); |
||||||||
536 | |||||||||
537 | return true; |
||||||||
538 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
539 | // SDK >=4.0 |
||||||||
540 | return false; |
||||||||
541 | } catch (\Couchbase\BaseException $e) { |
||||||||
542 | // SDK >=3.0 & <4.0 |
||||||||
543 | return false; |
||||||||
544 | } |
||||||||
545 | } |
||||||||
546 | |||||||||
547 | // SDK <3.0 |
||||||||
548 | try { |
||||||||
549 | $options = array('expiry' => $expire, 'cas' => $token); |
||||||||
550 | $result = $this->collection->replace($key, $value, $options); |
||||||||
551 | } catch (\CouchbaseException $e) { |
||||||||
552 | return false; |
||||||||
553 | } |
||||||||
554 | |||||||||
555 | $success = !$result->error; |
||||||||
556 | |||||||||
557 | // Couchbase is imprecise in its expiration handling, so we can clean up |
||||||||
558 | // stuff that is already expired (assuming the `cas` succeeded) |
||||||||
559 | if ($success) { |
||||||||
560 | $this->deleteIfExpired($key, $expire); |
||||||||
561 | } |
||||||||
562 | |||||||||
563 | return $success; |
||||||||
564 | } |
||||||||
565 | |||||||||
566 | /** |
||||||||
567 | * {@inheritdoc} |
||||||||
568 | */ |
||||||||
569 | public function increment($key, $offset = 1, $initial = 0, $expire = 0) |
||||||||
570 | { |
||||||||
571 | $this->assertValidKey($key); |
||||||||
572 | |||||||||
573 | if ($offset <= 0 || $initial < 0) { |
||||||||
574 | return false; |
||||||||
575 | } |
||||||||
576 | |||||||||
577 | return $this->doIncrement($key, $offset, $initial, $expire); |
||||||||
578 | } |
||||||||
579 | |||||||||
580 | /** |
||||||||
581 | * {@inheritdoc} |
||||||||
582 | */ |
||||||||
583 | public function decrement($key, $offset = 1, $initial = 0, $expire = 0) |
||||||||
584 | { |
||||||||
585 | $this->assertValidKey($key); |
||||||||
586 | |||||||||
587 | if ($offset <= 0 || $initial < 0) { |
||||||||
588 | return false; |
||||||||
589 | } |
||||||||
590 | |||||||||
591 | return $this->doIncrement($key, -$offset, $initial, $expire); |
||||||||
592 | } |
||||||||
593 | |||||||||
594 | /** |
||||||||
595 | * {@inheritdoc} |
||||||||
596 | */ |
||||||||
597 | public function touch($key, $expire) |
||||||||
598 | { |
||||||||
599 | $this->assertValidKey($key); |
||||||||
600 | |||||||||
601 | if ($this->deleteIfExpired($key, $expire)) { |
||||||||
602 | return true; |
||||||||
603 | } |
||||||||
604 | |||||||||
605 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
606 | // SDK >=3.0 |
||||||||
607 | try { |
||||||||
608 | $options = new \Couchbase\GetAndTouchOptions(); |
||||||||
0 ignored issues
–
show
The type
Couchbase\GetAndTouchOptions was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||||||||
609 | $options = null === $this->timeout ? $options : $options->timeout($this->timeout); |
||||||||
610 | $this->collection->getAndTouch($key, $expire, $options); |
||||||||
0 ignored issues
–
show
It seems like
$options can also be of type Couchbase\GetAndTouchOptions ; however, parameter $options of Couchbase\Bucket::getAndTouch() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||||
611 | |||||||||
612 | return true; |
||||||||
613 | } catch (\Couchbase\Exception\CouchbaseException $e) { |
||||||||
614 | // SDK >=4.0 |
||||||||
615 | return false; |
||||||||
616 | } catch (\Couchbase\BaseException $e) { |
||||||||
617 | // SDK >=3.0 & <4.0 |
||||||||
618 | return false; |
||||||||
619 | } |
||||||||
620 | } |
||||||||
621 | |||||||||
622 | // SDK <3.0 |
||||||||
623 | try { |
||||||||
624 | $result = $this->collection->getAndTouch($key, $expire); |
||||||||
625 | } catch (\CouchbaseException $e) { |
||||||||
626 | return false; |
||||||||
627 | } |
||||||||
628 | |||||||||
629 | return !$result->error; |
||||||||
630 | } |
||||||||
631 | |||||||||
632 | /** |
||||||||
633 | * {@inheritdoc} |
||||||||
634 | */ |
||||||||
635 | public function flush() |
||||||||
636 | { |
||||||||
637 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
638 | // SDK >=3.0 |
||||||||
639 | $bucketSettings = $this->bucketManager->getBucket($this->bucket->name()); |
||||||||
0 ignored issues
–
show
The method
getBucket() does not exist on Couchbase\BucketManager .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
The method
name() does not exist on Couchbase\Bucket .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||||
640 | if (!$bucketSettings->flushEnabled()) { |
||||||||
641 | // `enableFlush` exists, but whether or not it is enabled is config |
||||||||
642 | // that doesn't belong here; Scrapbook shouldn't alter that |
||||||||
643 | return false; |
||||||||
644 | } |
||||||||
645 | |||||||||
646 | $this->bucketManager->flush($this->bucket->name()); |
||||||||
647 | |||||||||
648 | return true; |
||||||||
649 | } |
||||||||
650 | |||||||||
651 | // SDK <3.0 |
||||||||
652 | // depending on config & client version, flush may not be available |
||||||||
653 | try { |
||||||||
654 | /* |
||||||||
655 | * Flush wasn't always properly implemented[1] in the client, plus |
||||||||
656 | * it depends on server config[2] to be enabled. Return status has |
||||||||
657 | * been null in both success & failure cases. |
||||||||
658 | * Flush is a very pervasive function that's likely not called |
||||||||
659 | * lightly. Since it's probably more important to know whether or |
||||||||
660 | * not it succeeded, than having it execute as fast as possible, I'm |
||||||||
661 | * going to add some calls and test if flush succeeded. |
||||||||
662 | * |
||||||||
663 | * 1: https://forums.couchbase.com/t/php-flush-isnt-doing-anything/1886/8 |
||||||||
664 | * 2: http://docs.couchbase.com/admin/admin/CLI/CBcli/cbcli-bucket-flush.html |
||||||||
665 | */ |
||||||||
666 | $this->collection->upsert('cb-flush-tester', ''); |
||||||||
667 | |||||||||
668 | if ($this->collection instanceof \Couchbase\Collection) { |
||||||||
669 | // SDK >=3.0 |
||||||||
670 | $this->bucketManager->flush($this->bucket->name()); |
||||||||
671 | } elseif (method_exists($this->bucketManager, 'flush')) { |
||||||||
672 | // SDK >=2.0.6 and <3.0 |
||||||||
673 | $this->bucketManager->flush(); |
||||||||
674 | } elseif (method_exists($this->collection, 'flush')) { |
||||||||
675 | // SDK <2.0.6 |
||||||||
676 | $this->collection->flush(); |
||||||||
677 | } else { |
||||||||
678 | return false; |
||||||||
679 | } |
||||||||
680 | } catch (\CouchbaseException $e) { |
||||||||
681 | return false; |
||||||||
682 | } |
||||||||
683 | |||||||||
684 | try { |
||||||||
685 | // cleanup in case flush didn't go through; but if it did, we won't |
||||||||
686 | // be able to remove it and know flush succeeded |
||||||||
687 | $result = $this->collection->remove('cb-flush-tester'); |
||||||||
688 | |||||||||
689 | return (bool) $result->error; |
||||||||
690 | } catch (\CouchbaseException $e) { |
||||||||
691 | // exception: "The key does not exist on the server" |
||||||||
692 | return true; |
||||||||
693 | } |
||||||||
694 | } |
||||||||
695 | |||||||||
696 | /** |
||||||||
697 | * {@inheritdoc} |
||||||||
698 | */ |
||||||||
699 | public function getCollection($name) |
||||||||
700 | { |
||||||||
701 | return new Collection($this, $name); |
||||||||
702 | } |
||||||||
703 | |||||||||
704 | /** |
||||||||
705 | * We could use `$this->collection->counter()`, but it doesn't seem to respect |
||||||||
706 | * data types and stores the values as strings instead of integers. |
||||||||
707 | * |
||||||||
708 | * Shared between increment/decrement: both have mostly the same logic |
||||||||
709 | * (decrement just increments a negative value), but need their validation |
||||||||
710 | * split up (increment won't accept negative values). |
||||||||
711 | * |
||||||||
712 | * @param string $key |
||||||||
713 | * @param int $offset |
||||||||
714 | * @param int $initial |
||||||||
715 | * @param int $expire |
||||||||
716 | * |
||||||||
717 | * @return int|bool |
||||||||
718 | */ |
||||||||
719 | protected function doIncrement($key, $offset, $initial, $expire) |
||||||||
720 | { |
||||||||
721 | $this->assertValidKey($key); |
||||||||
722 | |||||||||
723 | $value = $this->get($key, $token); |
||||||||
724 | if (false === $value) { |
||||||||
725 | $success = $this->add($key, $initial, $expire); |
||||||||
726 | |||||||||
727 | return $success ? $initial : false; |
||||||||
728 | } |
||||||||
729 | |||||||||
730 | if (!is_numeric($value) || $value < 0) { |
||||||||
731 | return false; |
||||||||
732 | } |
||||||||
733 | |||||||||
734 | $value += $offset; |
||||||||
735 | // value can never be lower than 0 |
||||||||
736 | $value = max(0, $value); |
||||||||
737 | $success = $this->cas($token, $key, $value, $expire); |
||||||||
738 | |||||||||
739 | return $success ? $value : false; |
||||||||
740 | } |
||||||||
741 | |||||||||
742 | /** |
||||||||
743 | * Couchbase doesn't properly remember the data type being stored: |
||||||||
744 | * arrays and objects are turned into stdClass instances, or the |
||||||||
745 | * other way around. |
||||||||
746 | * |
||||||||
747 | * @param mixed $value |
||||||||
748 | * |
||||||||
749 | * @return string|mixed |
||||||||
750 | */ |
||||||||
751 | protected function serialize($value) |
||||||||
752 | { |
||||||||
753 | // binary data doesn't roundtrip well |
||||||||
754 | if (is_string($value) && !preg_match('//u', $value)) { |
||||||||
755 | return serialize(base64_encode($value)); |
||||||||
756 | } |
||||||||
757 | |||||||||
758 | // and neither do arrays/objects |
||||||||
759 | if (is_array($value) || is_object($value)) { |
||||||||
760 | return serialize($value); |
||||||||
761 | } |
||||||||
762 | |||||||||
763 | return $value; |
||||||||
764 | } |
||||||||
765 | |||||||||
766 | /** |
||||||||
767 | * Restore serialized data. |
||||||||
768 | * |
||||||||
769 | * @param mixed $value |
||||||||
770 | * |
||||||||
771 | * @return mixed|int|float |
||||||||
772 | */ |
||||||||
773 | protected function unserialize($value) |
||||||||
774 | { |
||||||||
775 | // more efficient quick check whether value is unserializable |
||||||||
776 | if (!is_string($value) || !preg_match('/^[saOC]:[0-9]+:/', $value)) { |
||||||||
777 | return $value; |
||||||||
778 | } |
||||||||
779 | |||||||||
780 | $unserialized = @unserialize($value); |
||||||||
781 | if (false === $unserialized) { |
||||||||
782 | return $value; |
||||||||
783 | } |
||||||||
784 | |||||||||
785 | if (is_string($unserialized)) { |
||||||||
786 | return base64_decode($unserialized); |
||||||||
787 | } |
||||||||
788 | |||||||||
789 | return $unserialized; |
||||||||
790 | } |
||||||||
791 | |||||||||
792 | /** |
||||||||
793 | * Couchbase seems to not timely purge items the way it should when |
||||||||
794 | * storing it with an expired timestamp, so we'll detect that and |
||||||||
795 | * delete it (instead of performing the already expired operation). |
||||||||
796 | * |
||||||||
797 | * @param string|string[] $key |
||||||||
798 | * @param int $expire |
||||||||
799 | * |
||||||||
800 | * @return bool True if expired |
||||||||
801 | */ |
||||||||
802 | protected function deleteIfExpired($key, $expire) |
||||||||
803 | { |
||||||||
804 | if ($expire < 0 || ($expire > 2592000 && $expire < time())) { |
||||||||
805 | $this->deleteMulti((array) $key); |
||||||||
806 | |||||||||
807 | return true; |
||||||||
808 | } |
||||||||
809 | |||||||||
810 | return false; |
||||||||
811 | } |
||||||||
812 | |||||||||
813 | /** |
||||||||
814 | * @param string $key |
||||||||
815 | * |
||||||||
816 | * @throws InvalidKey |
||||||||
817 | */ |
||||||||
818 | protected function assertValidKey($key) |
||||||||
819 | { |
||||||||
820 | if (strlen($key) > 255) { |
||||||||
821 | throw new InvalidKey("Invalid key: $key. Couchbase keys can not exceed 255 chars."); |
||||||||
822 | } |
||||||||
823 | } |
||||||||
824 | } |
||||||||
825 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths