summaryrefslogtreecommitdiff
path: root/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php')
-rw-r--r--vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php198
1 files changed, 198 insertions, 0 deletions
diff --git a/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php b/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
new file mode 100644
index 0000000..9646bc3
--- /dev/null
+++ b/vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
@@ -0,0 +1,198 @@
1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Cache\Adapter;
13
14use Couchbase\Bucket;
15use Couchbase\Cluster;
16use Couchbase\ClusterOptions;
17use Couchbase\Collection;
18use Couchbase\DocumentNotFoundException;
19use Couchbase\UpsertOptions;
20use Symfony\Component\Cache\Exception\CacheException;
21use Symfony\Component\Cache\Exception\InvalidArgumentException;
22use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
23use Symfony\Component\Cache\Marshaller\MarshallerInterface;
24
25/**
26 * @author Antonio Jose Cerezo Aranda <aj.cerezo@gmail.com>
27 */
28class CouchbaseCollectionAdapter extends AbstractAdapter
29{
30 private const MAX_KEY_LENGTH = 250;
31
32 private MarshallerInterface $marshaller;
33
34 public function __construct(
35 private Collection $connection,
36 string $namespace = '',
37 int $defaultLifetime = 0,
38 ?MarshallerInterface $marshaller = null,
39 ) {
40 if (!static::isSupported()) {
41 throw new CacheException('Couchbase >= 3.0.5 < 4.0.0 is required.');
42 }
43
44 $this->maxIdLength = static::MAX_KEY_LENGTH;
45
46 parent::__construct($namespace, $defaultLifetime);
47 $this->enableVersioning();
48 $this->marshaller = $marshaller ?? new DefaultMarshaller();
49 }
50
51 public static function createConnection(#[\SensitiveParameter] array|string $dsn, array $options = []): Bucket|Collection
52 {
53 if (\is_string($dsn)) {
54 $dsn = [$dsn];
55 }
56
57 if (!static::isSupported()) {
58 throw new CacheException('Couchbase >= 3.0.5 < 4.0.0 is required.');
59 }
60
61 set_error_handler(static fn ($type, $msg, $file, $line) => throw new \ErrorException($msg, 0, $type, $file, $line));
62
63 $pathPattern = '/^(?:\/(?<bucketName>[^\/\?]+))(?:(?:\/(?<scopeName>[^\/]+))(?:\/(?<collectionName>[^\/\?]+)))?(?:\/)?$/';
64 $newServers = [];
65 $protocol = 'couchbase';
66 try {
67 $username = $options['username'] ?? '';
68 $password = $options['password'] ?? '';
69
70 foreach ($dsn as $server) {
71 if (!str_starts_with($server, 'couchbase:')) {
72 throw new InvalidArgumentException('Invalid Couchbase DSN: it does not start with "couchbase:".');
73 }
74
75 $params = parse_url($server);
76
77 $username = isset($params['user']) ? rawurldecode($params['user']) : $username;
78 $password = isset($params['pass']) ? rawurldecode($params['pass']) : $password;
79 $protocol = $params['scheme'] ?? $protocol;
80
81 if (isset($params['query'])) {
82 $optionsInDsn = self::getOptions($params['query']);
83
84 foreach ($optionsInDsn as $parameter => $value) {
85 $options[$parameter] = $value;
86 }
87 }
88
89 $newServers[] = $params['host'];
90 }
91
92 $option = isset($params['query']) ? '?'.$params['query'] : '';
93 $connectionString = $protocol.'://'.implode(',', $newServers).$option;
94
95 $clusterOptions = new ClusterOptions();
96 $clusterOptions->credentials($username, $password);
97
98 $client = new Cluster($connectionString, $clusterOptions);
99
100 preg_match($pathPattern, $params['path'] ?? '', $matches);
101 $bucket = $client->bucket($matches['bucketName']);
102 $collection = $bucket->defaultCollection();
103 if (!empty($matches['scopeName'])) {
104 $scope = $bucket->scope($matches['scopeName']);
105 $collection = $scope->collection($matches['collectionName']);
106 }
107
108 return $collection;
109 } finally {
110 restore_error_handler();
111 }
112 }
113
114 public static function isSupported(): bool
115 {
116 return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0.5', '>=') && version_compare(phpversion('couchbase'), '4.0', '<');
117 }
118
119 private static function getOptions(string $options): array
120 {
121 $results = [];
122 $optionsInArray = explode('&', $options);
123
124 foreach ($optionsInArray as $option) {
125 [$key, $value] = explode('=', $option);
126
127 $results[$key] = $value;
128 }
129
130 return $results;
131 }
132
133 protected function doFetch(array $ids): array
134 {
135 $results = [];
136 foreach ($ids as $id) {
137 try {
138 $resultCouchbase = $this->connection->get($id);
139 } catch (DocumentNotFoundException) {
140 continue;
141 }
142
143 $content = $resultCouchbase->value ?? $resultCouchbase->content();
144
145 $results[$id] = $this->marshaller->unmarshall($content);
146 }
147
148 return $results;
149 }
150
151 protected function doHave($id): bool
152 {
153 return $this->connection->exists($id)->exists();
154 }
155
156 protected function doClear($namespace): bool
157 {
158 return false;
159 }
160
161 protected function doDelete(array $ids): bool
162 {
163 $idsErrors = [];
164 foreach ($ids as $id) {
165 try {
166 $result = $this->connection->remove($id);
167
168 if (null === $result->mutationToken()) {
169 $idsErrors[] = $id;
170 }
171 } catch (DocumentNotFoundException) {
172 }
173 }
174
175 return 0 === \count($idsErrors);
176 }
177
178 protected function doSave(array $values, $lifetime): array|bool
179 {
180 if (!$values = $this->marshaller->marshall($values, $failed)) {
181 return $failed;
182 }
183
184 $upsertOptions = new UpsertOptions();
185 $upsertOptions->expiry($lifetime);
186
187 $ko = [];
188 foreach ($values as $key => $value) {
189 try {
190 $this->connection->upsert($key, $value, $upsertOptions);
191 } catch (\Exception) {
192 $ko[$key] = '';
193 }
194 }
195
196 return [] === $ko ? true : $ko;
197 }
198}