Passed
Push — master ( c82647...c877fa )
by Georges
11:01
created

Driver::setBucket()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 *
5
 * This file is part of Phpfastcache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files.
10
 *
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Contributors  https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phpfastcache\Drivers\Couchbasev3;
18
19
use Couchbase\BaseException as CouchbaseException;
20
use Couchbase\Bucket as CouchbaseBucket;
21
use Couchbase\Cluster;
22
use Couchbase\ClusterOptions;
23
use Couchbase\Collection;
24
use Couchbase\DocumentNotFoundException;
25
use Couchbase\GetResult;
26
use Couchbase\Scope;
27
use Couchbase\UpsertOptions;
28
use DateTimeInterface;
29
use Phpfastcache\Cluster\AggregatablePoolInterface;
30
use Phpfastcache\Config\ConfigurationOption;
31
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
32
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
33
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
34
use Phpfastcache\Entities\DriverStatistic;
35
use Phpfastcache\Event\EventManagerInterface;
36
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
37
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
38
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
39
use Phpfastcache\Exceptions\PhpfastcacheUnsupportedException;
40
use Phpfastcache\Exceptions\PhpfastcacheUnsupportedMethodException;
41
42
/**
43
 * @property Cluster $instance Instance of driver service
44
 * @method Config getConfig()
45
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
46
 */
47
class Driver implements AggregatablePoolInterface
48
{
49
    use TaggableCacheItemPoolTrait;
50
51
    protected Scope $scope;
52
53
    protected Collection $collection;
54
55
    protected CouchbaseBucket $bucketInstance;
56
57
    /**
58
     * @return bool
59
     */
60
    public function driverCheck(): bool
61
    {
62
        return extension_loaded('couchbase');
63
    }
64
65
    /**
66
     * @return bool
67
     * @throws PhpfastcacheDriverCheckException
68
     */
69
    protected function driverConnect(): bool
70
    {
71
        if (!\class_exists(ClusterOptions::class)) {
72
            throw new PhpfastcacheDriverCheckException('You are using the Couchbase PHP SDK 2.x which is no longer supported in Phpfastcache v9');
73
        }
74
75
        $connectionString = "couchbase://{$this->getConfig()->getHost()}:{$this->getConfig()->getPort()}";
76
77
        $options = new ClusterOptions();
78
        $options->credentials($this->getConfig()->getUsername(), $this->getConfig()->getPassword());
79
        $this->instance = new Cluster($connectionString, $options);
0 ignored issues
show
Unused Code introduced by
The call to Couchbase\Cluster::__construct() has too many arguments starting with $options. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
        $this->instance = /** @scrutinizer ignore-call */ new Cluster($connectionString, $options);

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

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

Loading history...
80
81
        $this->setBucket($this->instance->bucket($this->getConfig()->getBucketName()));
0 ignored issues
show
Bug introduced by
The method bucket() does not exist on Couchbase\Cluster. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
        $this->setBucket($this->instance->/** @scrutinizer ignore-call */ bucket($this->getConfig()->getBucketName()));

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...
82
        $this->setScope($this->getBucket()->scope($this->getConfig()->getScopeName()));
0 ignored issues
show
Bug introduced by
The method scope() 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 ignore-call  annotation

82
        $this->setScope($this->getBucket()->/** @scrutinizer ignore-call */ scope($this->getConfig()->getScopeName()));

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...
83
        $this->setCollection($this->getScope()->collection($this->getConfig()->getCollectionName()));
84
85
        return true;
86
    }
87
88
    /**
89
     * @param ExtendedCacheItemInterface $item
90
     * @return ?array<string, mixed>
91
     */
92
    protected function driverRead(ExtendedCacheItemInterface $item): ?array
93
    {
94
        try {
95
            /**
96
             * CouchbaseBucket::get() returns a GetResult interface
97
             */
98
            return $this->decodeDocument((array)$this->getCollection()->get($item->getEncodedKey())->content());
99
        } catch (DocumentNotFoundException) {
100
            return null;
101
        }
102
    }
103
104
    /**
105
     * @param ExtendedCacheItemInterface $item
106
     * @return array<array<string, mixed>>
107
     */
108
    protected function driverReadMultiple(ExtendedCacheItemInterface ...$items): array
109
    {
110
        try {
111
            $results = [];
112
            /**
113
             * CouchbaseBucket::get() returns a GetResult interface
114
             */
115
            /** @var GetResult $document */
116
            foreach ($this->getCollection()->getMulti($this->getKeys($items, true)) as $document) {
117
                $content = $document->content();
118
                if ($content) {
119
                    $decodedDocument = $this->decodeDocument($content);
120
                    $results[$decodedDocument[ExtendedCacheItemPoolInterface::DRIVER_KEY_WRAPPER_INDEX]] = $this->decodeDocument($content);
121
                }
122
            }
123
124
            return $results;
125
        } catch (DocumentNotFoundException) {
126
            return [];
127
        }
128
    }
129
130
    /**
131
     * @param ExtendedCacheItemInterface $item
132
     * @return bool
133
     * @throws PhpfastcacheInvalidArgumentException
134
     * @throws PhpfastcacheLogicException
135
     */
136
    protected function driverWrite(ExtendedCacheItemInterface $item): bool
137
    {
138
139
        try {
140
            $this->getCollection()->upsert(
141
                $item->getEncodedKey(),
142
                $this->encodeDocument($this->driverPreWrap($item)),
143
                (new UpsertOptions())->expiry($item->getTtl())
144
            );
145
            return true;
146
        } catch (CouchbaseException) {
147
            return false;
148
        }
149
    }
150
151
    /**
152
     * @param string $key
153
     * @param string $encodedKey
154
     * @return bool
155
     */
156
    protected function driverDelete(string $key, string $encodedKey): bool
157
    {
158
159
        try {
160
            return $this->getCollection()->remove($encodedKey)->mutationToken() !== null;
161
        } catch (DocumentNotFoundException) {
162
            return true;
163
        } catch (CouchbaseException) {
164
            return false;
165
        }
166
    }
167
168
169
    /**
170
     * @param string[] $keys
171
     * @return bool
172
     */
173
    protected function driverDeleteMultiple(array $keys): bool
174
    {
175
        try {
176
            $this->getCollection()->removeMulti(array_map(fn(string $key) => $this->getEncodedKey($key), $keys));
177
            return true;
178
        } catch (CouchbaseException) {
179
            return false;
180
        }
181
    }
182
183
184
    /**
185
     * @return bool
186
     * @throws PhpfastcacheUnsupportedMethodException
187
     */
188
    protected function driverClear(): bool
189
    {
190
        if (!$this->instance->buckets()->getBucket($this->getConfig()->getBucketName())->flushEnabled()) {
0 ignored issues
show
Bug introduced by
The method buckets() does not exist on Couchbase\Cluster. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

190
        if (!$this->instance->/** @scrutinizer ignore-call */ buckets()->getBucket($this->getConfig()->getBucketName())->flushEnabled()) {

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...
191
            $this->instance->buckets()->getBucket($this->getConfig()->getBucketName())->enableFlush(true);
192
            /** @phpstan-ignore-next-line */
193
            if (!$this->instance->buckets()->getBucket($this->getConfig()->getBucketName())->flushEnabled()) {
194
                throw new PhpfastcacheUnsupportedMethodException(
195
                    'Flushing operation is not enabled on your Bucket. See https://docs.couchbase.com/server/current/manage/manage-buckets/flush-bucket.html'
196
                );
197
            }
198
        }
199
200
        $this->instance->buckets()->flush($this->getConfig()->getBucketName());
201
202
        return true;
203
    }
204
205
    /**
206
     * @return DriverStatistic
207
     * @throws \Exception
208
     */
209
    public function getStats(): DriverStatistic
210
    {
211
        /**
212
         * Between SDK 2 and 3 we lost a lot of useful information :(
213
         * @see https://docs.couchbase.com/java-sdk/current/project-docs/migrating-sdk-code-to-3.n.html#management-apis
214
         */
215
        $info = $this->getBucket()->diagnostics(\bin2hex(\random_bytes(16)));
0 ignored issues
show
Bug introduced by
The method diagnostics() 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 ignore-call  annotation

215
        $info = $this->getBucket()->/** @scrutinizer ignore-call */ diagnostics(\bin2hex(\random_bytes(16)));

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...
216
217
        return (new DriverStatistic())
218
            ->setSize(0)
219
            ->setRawData($info)
220
            ->setData(implode(', ', array_keys($this->itemInstances)))
221
            ->setInfo($info['sdk'] . "\n For more information see RawData.");
222
    }
223
224
    /**
225
     * @return Collection
226
     */
227
    public function getCollection(): Collection
228
    {
229
        return $this->collection;
230
    }
231
232
    /**
233
     * @param Collection $collection
234
     * @return Driver
235
     */
236
    public function setCollection(Collection $collection): Driver
237
    {
238
        $this->collection = $collection;
239
        return $this;
240
    }
241
242
    /**
243
     * @return Scope
244
     */
245
    public function getScope(): Scope
246
    {
247
        return $this->scope;
248
    }
249
250
    /**
251
     * @param Scope $scope
252
     * @return Driver
253
     */
254
    public function setScope(Scope $scope): Driver
255
    {
256
        $this->scope = $scope;
257
        return $this;
258
    }
259
260
    /**
261
     * @return CouchbaseBucket
262
     */
263
    protected function getBucket(): CouchbaseBucket
264
    {
265
        return $this->bucketInstance;
266
    }
267
268
    /**
269
     * @param CouchbaseBucket $couchbaseBucket
270
     */
271
    protected function setBucket(CouchbaseBucket $couchbaseBucket): void
272
    {
273
        $this->bucketInstance = $couchbaseBucket;
274
    }
275
276
277
    /**
278
     * @param array<string, mixed> $data
279
     * @return array<string, mixed>
280
     */
281
    protected function encodeDocument(array $data): array
282
    {
283
        $data[ExtendedCacheItemPoolInterface::DRIVER_DATA_WRAPPER_INDEX] = $this->encode($data[ExtendedCacheItemPoolInterface::DRIVER_DATA_WRAPPER_INDEX]);
284
        $data[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX] = $data[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX]
285
            ->format(DateTimeInterface::ATOM);
286
287
        if ($this->getConfig()->isItemDetailedDate()) {
288
            $data[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX] = $data[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX]
289
                ->format(\DateTimeInterface::ATOM);
290
291
            $data[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX] = $data[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX]
292
                ->format(\DateTimeInterface::ATOM);
293
        }
294
295
        return $data;
296
    }
297
298
    /**
299
     * @param array<string, mixed> $data
300
     * @return array<string, mixed>
301
     */
302
    protected function decodeDocument(array $data): array
303
    {
304
        $data[ExtendedCacheItemPoolInterface::DRIVER_DATA_WRAPPER_INDEX] = $this->unserialize($data[ExtendedCacheItemPoolInterface::DRIVER_DATA_WRAPPER_INDEX]);
305
        $data[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX] = \DateTime::createFromFormat(
306
            \DateTimeInterface::ATOM,
307
            $data[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX]
308
        );
309
310
        if ($this->getConfig()->isItemDetailedDate()) {
311
            $data[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX] = \DateTime::createFromFormat(
312
                \DateTimeInterface::ATOM,
313
                $data[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX]
314
            );
315
316
            $data[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX] = \DateTime::createFromFormat(
317
                \DateTimeInterface::ATOM,
318
                $data[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX]
319
            );
320
        }
321
322
        return $data;
323
    }
324
}
325