Passed
Push — v9 ( cdb3b5...611ee9 )
by Georges
02:15
created

Driver::getConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
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
declare(strict_types=1);
15
16
namespace Phpfastcache\Drivers\Arangodb;
17
18
use ArangoDBClient\Collection as ArangoCollection;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\Collection 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use ArangoDBClient\CollectionHandler as ArangoCollectionHandler;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\CollectionHandler 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use ArangoDBClient\Connection as ArangoConnection;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\Connection 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use ArangoDBClient\ConnectionOptions as ArangoConnectionOptions;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\ConnectionOptions 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use ArangoDBClient\Document as ArangoDocument;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\Document 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use ArangoDBClient\DocumentHandler as ArangoDocumentHandler;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\DocumentHandler 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use ArangoDBClient\Exception as ArangoException;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\Exception 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use ArangoDBClient\ServerException as ArangoServerException;
0 ignored issues
show
Bug introduced by
The type ArangoDBClient\ServerException 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
27
use Phpfastcache\Cluster\AggregatablePoolInterface;
28
use Phpfastcache\Config\ConfigurationOption;
29
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
30
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
31
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
32
use Phpfastcache\Entities\DriverStatistic;
33
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException;
34
use Phpfastcache\Exceptions\PhpfastcacheDriverException;
35
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
36
37
/**
38
 * Class Driver
39
 * @property Config $config
40
 * @property ArangoConnection $instance
41
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
42
 */
43
class Driver implements ExtendedCacheItemPoolInterface, AggregatablePoolInterface
44
{
45
    use TaggableCacheItemPoolTrait;
46
47
    protected const TTL_FIELD_NAME = 't';
48
49
    protected ArangoDocumentHandler $documentHandler;
50
    protected ArangoCollectionHandler $collectionHandler;
51
52
    /**
53
     * @return bool
54
     */
55
    public function driverCheck(): bool
56
    {
57
        return \class_exists(ArangoConnection::class);
58
    }
59
60
    /**
61
     * @return bool
62
     * @throws ArangoException
63
     * @throws PhpfastcacheDriverConnectException
64
     */
65
    protected function driverConnect(): bool
66
    {
67
        /** @var Config $config */
68
        $config = $this->getConfig();
69
70
        $connectionOptions = [
71
            ArangoConnectionOptions::OPTION_DATABASE => $config->getDatabase(),
72
            ArangoConnectionOptions::OPTION_ENDPOINT => $config->getEndpoint(),
73
74
            ArangoConnectionOptions::OPTION_CONNECTION  => $config->getConnection(),
75
            ArangoConnectionOptions::OPTION_AUTH_TYPE   => $config->getAuthType(),
76
            ArangoConnectionOptions::OPTION_AUTH_USER   => $config->getAuthUser(),
77
            ArangoConnectionOptions::OPTION_AUTH_PASSWD => $config->getAuthPasswd(),
78
79
            ArangoConnectionOptions::OPTION_CONNECT_TIMEOUT => $config->getConnectTimeout(),
80
            ArangoConnectionOptions::OPTION_REQUEST_TIMEOUT => $config->getRequestTimeout(),
81
            ArangoConnectionOptions::OPTION_CREATE        => $config->isAutoCreate(),
82
            ArangoConnectionOptions::OPTION_UPDATE_POLICY => $config->getUpdatePolicy(),
83
84
            // Options below are not yet supported
85
            // ConnectionOptions::OPTION_MEMCACHED_PERSISTENT_ID => 'arangodb-php-pool',
86
            // ConnectionOptions::OPTION_MEMCACHED_SERVERS       => [ [ '127.0.0.1', 11211 ] ],
87
            // ConnectionOptions::OPTION_MEMCACHED_OPTIONS       => [ ],
88
            // ConnectionOptions::OPTION_MEMCACHED_ENDPOINTS_KEY => 'arangodb-php-endpoints'
89
            // ConnectionOptions::OPTION_MEMCACHED_TTL           => 600
90
        ];
91
92
        if ($config->getTraceFunction() !== null) {
93
            $connectionOptions[ArangoConnectionOptions::OPTION_TRACE] = $config->getTraceFunction();
94
        }
95
96
        if ($config->getAuthJwt() !== null) {
97
            $connectionOptions[ArangoConnectionOptions::OPTION_AUTH_JWT] = $config->getAuthJwt();
98
        }
99
100
        if (\str_starts_with($config->getAuthType(), 'ssl://')) {
101
            $connectionOptions[ArangoConnectionOptions::OPTION_VERIFY_CERT] = $config->isVerifyCert();
102
            $connectionOptions[ArangoConnectionOptions::OPTION_ALLOW_SELF_SIGNED] = $config->isSelfSigned();
103
            $connectionOptions[ArangoConnectionOptions::OPTION_CIPHERS] = $config->getCiphers();
104
        }
105
106
        $this->instance = new ArangoConnection($connectionOptions);
107
        $this->documentHandler = new ArangoDocumentHandler($this->instance);
108
        $this->collectionHandler = new ArangoCollectionHandler($this->instance);
109
110
        $collectionNames = array_keys($this->collectionHandler->getAllCollections());
111
112
        if ($config->isAutoCreate() && !\in_array($config->getCollection(), $collectionNames, true)) {
113
            return $this->createCollection($config->getCollection());
114
        }
115
116
        return $this->collectionHandler->has($config->getCollection());
117
    }
118
119
    /**
120
     * @param ExtendedCacheItemInterface $item
121
     * @return bool
122
     * @throws ArangoException
123
     * @throws PhpfastcacheLogicException
124
     */
125
    protected function driverWrite(ExtendedCacheItemInterface $item): bool
126
    {
127
        /** @var Config $config */
128
        $config = $this->getConfig();
129
        $options = [
130
            'overwriteMode' => 'replace',
131
            'returnNew' => true,
132
            'returnOld' => false,
133
            'silent' => false,
134
        ];
135
136
        $document = new ArangoDocument();
137
        $document->setInternalKey($item->getEncodedKey());
138
        $document->set(self::DRIVER_KEY_WRAPPER_INDEX, $item->getKey());
139
        $document->set(self::DRIVER_DATA_WRAPPER_INDEX, $this->encode($item->get()));
140
        $document->set(self::DRIVER_TAGS_WRAPPER_INDEX, $item->getTags());
141
        $document->set(self::DRIVER_EDATE_WRAPPER_INDEX, $item->getExpirationDate());
142
        $document->set(self::TTL_FIELD_NAME, $item->getExpirationDate()->getTimestamp());
143
144
        if ($config->isItemDetailedDate()) {
145
            $document->set(self::DRIVER_CDATE_WRAPPER_INDEX, $item->getCreationDate());
146
            $document->set(self::DRIVER_MDATE_WRAPPER_INDEX, $item->getModificationDate());
147
        }
148
149
        return $this->documentHandler->insert($config->getCollection(), $document, $options) !== null;
150
    }
151
152
    /**
153
     * @param ExtendedCacheItemInterface $item
154
     * @return null|array
155
     * @throws PhpfastcacheDriverException
156
     * @throws \Exception
157
     */
158
    protected function driverRead(ExtendedCacheItemInterface $item): ?array
159
    {
160
        /** @var Config $config */
161
        $config = $this->getConfig();
162
163
        try {
164
            $document = $this->documentHandler->get($config->getCollection(), $item->getEncodedKey());
165
        } catch (ArangoServerException $e) {
166
            if ($e->getCode() === 404) {
167
                return null;
168
            }
169
            throw new PhpfastcacheDriverException(
170
                'Got unexpeced error from Arangodb: ' . $e->getMessage()
171
            );
172
        }
173
174
        return $this->decode($document);
175
    }
176
177
    /**
178
     * @param ExtendedCacheItemInterface $item
179
     * @return bool
180
     */
181
    protected function driverDelete(ExtendedCacheItemInterface $item): bool
182
    {
183
        /** @var Config $config */
184
        $config = $this->getConfig();
185
186
        $options = [
187
            'returnOld' => false
188
        ];
189
190
        try {
191
            $this->documentHandler->removeById($config->getCollection(), $item->getEncodedKey(), null, $options);
192
            return true;
193
        } catch (ArangoException) {
194
            return false;
195
        }
196
    }
197
198
    /**
199
     * @return bool
200
     */
201
    protected function driverClear(): bool
202
    {
203
        /** @var Config $config */
204
        $config = $this->getConfig();
205
206
        try {
207
            $this->collectionHandler->truncate($config->getCollection());
208
            return true;
209
        } catch (ArangoException) {
210
            return false;
211
        }
212
    }
213
214
    /**
215
     * @throws PhpfastcacheDriverConnectException
216
     * @throws ArangoException
217
     */
218
    protected function createCollection($collectionName): bool
219
    {
220
        $collection = new ArangoCollection($collectionName);
221
222
        try {
223
            $this->collectionHandler->create($collection, [
224
                'type' => ArangoCollection::TYPE_DOCUMENT,
225
                'waitForSync' => false
226
            ]);
227
228
            $this->collectionHandler->createIndex($collection, [
229
                'type'         => 'ttl',
230
                'name'         => 'expires_at',
231
                'fields'       => [self::TTL_FIELD_NAME],
232
                'unique'       => false,
233
                'sparse'       => true,
234
                'inBackground' => true,
235
                'expireAfter' => 1
236
            ]);
237
            return true;
238
        } catch (\Throwable $e) {
239
            throw new PhpfastcacheDriverConnectException(
240
                sprintf(
241
                    'Unable to automatically create the collection, error returned from ArangoDB: [%d] %s',
242
                    $e->getCode(),
243
                    $e->getMessage(),
244
                )
245
            );
246
        }
247
    }
248
249
    /**
250
     * @param ArangoDocument $document
251
     * @return mixed
252
     * @throws \Exception
253
     */
254
    protected function decode(ArangoDocument $document): mixed
255
    {
256
        /** @var Config $config */
257
        $config = $this->getConfig();
258
259
        $value = [
260
            self::DRIVER_KEY_WRAPPER_INDEX => $document->get(self::DRIVER_KEY_WRAPPER_INDEX),
261
            self::DRIVER_TAGS_WRAPPER_INDEX => $document->get(self::DRIVER_TAGS_WRAPPER_INDEX),
262
            self::DRIVER_DATA_WRAPPER_INDEX => \unserialize(
263
                $document->get(self::DRIVER_DATA_WRAPPER_INDEX),
264
                ['allowed_classes' => true]
265
            ),
266
        ];
267
268
        $eDate = $document->get(self::DRIVER_EDATE_WRAPPER_INDEX);
269
        $value[ExtendedCacheItemPoolInterface::DRIVER_EDATE_WRAPPER_INDEX] = new \DateTime(
270
            $eDate['date'],
271
            new \DateTimeZone($eDate['timezone'])
272
        );
273
274
        if ($config->isItemDetailedDate()) {
275
            $cDate = $document->get(self::DRIVER_CDATE_WRAPPER_INDEX);
276
            if (isset($cDate['date'], $cDate['timezone'])) {
277
                $value[ExtendedCacheItemPoolInterface::DRIVER_CDATE_WRAPPER_INDEX] = new \DateTime(
278
                    $cDate['date'],
279
                    new \DateTimeZone($cDate['timezone'])
280
                );
281
            }
282
283
            $mDate = $document->get(self::DRIVER_MDATE_WRAPPER_INDEX);
284
            if (isset($mDate['date'], $cDate['timezone'])) {
285
                $value[ExtendedCacheItemPoolInterface::DRIVER_MDATE_WRAPPER_INDEX] = new \DateTime(
286
                    $mDate['date'],
287
                    new \DateTimeZone($mDate['timezone'])
288
                );
289
            }
290
        }
291
292
        return $value;
293
    }
294
295
    public function getStats(): DriverStatistic
296
    {
297
        /** @var Config $config */
298
        $config = $this->getConfig();
299
300
        $count = $this->collectionHandler->count($config->getCollection(), false);
301
        $info = $this->collectionHandler->get($config->getCollection());
302
303
        return (new DriverStatistic())
304
            ->setData(implode(', ', array_keys($this->itemInstances)))
305
            ->setInfo('No readable human data provided by ArangoDB :(')
306
            ->setRawData([$count, $info])
307
            ->setSize($count);
308
    }
309
310
    public function getConfig() : Config|ConfigurationOption
311
    {
312
        return $this->config;
313
    }
314
}
315