Driver   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 133
dl 0
loc 280
rs 10
c 1
b 0
f 0
wmc 21

9 Methods

Rating   Name   Duplication   Size   Complexity  
A driverCheck() 0 3 2
A driverRead() 0 23 5
A driverConnect() 0 64 4
A driverWrite() 0 45 2
A getCompatibleExecutionOptionsArgument() 0 7 2
A driverDelete() 0 30 2
A getHelp() 0 3 1
A getStats() 0 18 1
A driverClear() 0 17 2
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\Cassandra;
18
19
use Cassandra;
20
use Cassandra\Exception;
21
use Cassandra\Exception\InvalidArgumentException;
22
use Cassandra\Session as CassandraSession;
23
use DateTime;
24
use Phpfastcache\Cluster\AggregatablePoolInterface;
25
use Phpfastcache\Core\Item\ExtendedCacheItemInterface;
26
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
27
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait;
28
use Phpfastcache\Entities\DriverStatistic;
29
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
30
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
31
32
/**
33
 * Class Driver
34
 * @property CassandraSession|null $instance Instance of driver service
35
 * @method Config getConfig()
36
 */
37
class Driver implements AggregatablePoolInterface
38
{
39
    use TaggableCacheItemPoolTrait;
40
41
    protected const CASSANDRA_KEY_SPACE = 'phpfastcache';
42
    protected const CASSANDRA_TABLE = 'cacheItems';
43
44
    /**
45
     * @return bool
46
     */
47
    public function driverCheck(): bool
48
    {
49
        return extension_loaded('Cassandra') && class_exists(Cassandra::class);
50
    }
51
52
    /**
53
     * @return bool
54
     * @throws PhpfastcacheLogicException
55
     * @throws Exception
56
     */
57
    protected function driverConnect(): bool
58
    {
59
        $clientConfig = $this->getConfig();
60
61
        $clusterBuilder = Cassandra::cluster()
62
            ->withContactPoints($clientConfig->getHost())
63
            ->withPort($clientConfig->getPort());
64
65
        if (!empty($clientConfig->isSslEnabled())) {
66
            $sslBuilder = Cassandra::ssl();
67
            if (!empty($clientConfig->isSslVerify())) {
68
                $sslBuilder->withVerifyFlags(Cassandra::VERIFY_PEER_CERT);
69
            } else {
70
                $sslBuilder->withVerifyFlags(Cassandra::VERIFY_NONE);
71
            }
72
73
            $clusterBuilder->withSSL($sslBuilder->build());
74
        }
75
76
        $clusterBuilder->withConnectTimeout($clientConfig->getTimeout());
77
78
        if ($clientConfig->getUsername()) {
79
            $clusterBuilder->withCredentials($clientConfig->getUsername(), $clientConfig->getPassword());
80
        }
81
82
        $this->instance = $clusterBuilder->build()->connect('');
83
84
        /**
85
         * In case of emergency:
86
         * $this->instance->execute(
87
         *      new Cassandra\SimpleStatement(\sprintf("DROP KEYSPACE %s;", self::CASSANDRA_KEY_SPACE))
88
         * );
89
         */
90
91
        $this->instance->execute(
92
            new Cassandra\SimpleStatement(
93
                sprintf(
94
                    "CREATE KEYSPACE IF NOT EXISTS %s WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };",
95
                    self::CASSANDRA_KEY_SPACE
96
                )
97
            ),
98
            []
99
        );
100
        $this->instance->execute(new Cassandra\SimpleStatement(sprintf('USE %s;', self::CASSANDRA_KEY_SPACE)), []);
101
        $this->instance->execute(
102
            new Cassandra\SimpleStatement(
103
                sprintf(
104
                    '
105
                CREATE TABLE IF NOT EXISTS %s (
106
                    cache_uuid uuid,
107
                    cache_id varchar,
108
                    cache_data text,
109
                    cache_creation_date timestamp,
110
                    cache_expiration_date timestamp,
111
                    cache_length int,
112
                    PRIMARY KEY (cache_id)
113
                );',
114
                    self::CASSANDRA_TABLE
115
                )
116
            ),
117
            []
118
        );
119
120
        return true;
121
    }
122
123
    /**
124
     * @param ExtendedCacheItemInterface $item
125
     * @return ?array<string, mixed>
126
     */
127
    protected function driverRead(ExtendedCacheItemInterface $item): ?array
128
    {
129
        try {
130
            $options = $this->getCompatibleExecutionOptionsArgument(
131
                [
132
                    'arguments' => ['cache_id' => $item->getKey()],
133
                    'page_size' => 1,
134
                ]
135
            );
136
            $query = sprintf(
137
                'SELECT cache_data FROM %s.%s WHERE cache_id = :cache_id;',
138
                self::CASSANDRA_KEY_SPACE,
139
                self::CASSANDRA_TABLE
140
            );
141
            $results = $this->instance->execute(new Cassandra\SimpleStatement($query), $options);
0 ignored issues
show
Bug introduced by
The method execute() does not exist on null. ( Ignorable by Annotation )

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

141
            /** @scrutinizer ignore-call */ 
142
            $results = $this->instance->execute(new Cassandra\SimpleStatement($query), $options);

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...
142
143
            if ($results instanceof Cassandra\Rows && $results->count() === 1) {
144
                return $this->decode($results->first()['cache_data']) ?: null;
145
            }
146
147
            return null;
148
        } catch (Exception $e) {
149
            return null;
150
        }
151
    }
152
153
    /**
154
     * @param ExtendedCacheItemInterface $item
155
     * @return bool
156
     * @throws PhpfastcacheInvalidArgumentException
157
     */
158
    protected function driverWrite(ExtendedCacheItemInterface $item): bool
159
    {
160
161
        try {
162
            $cacheData = $this->encode($this->driverPreWrap($item));
163
            $options = $this->getCompatibleExecutionOptionsArgument(
164
                [
165
                    'arguments' => [
166
                        'cache_uuid' => new Cassandra\Uuid(''),
167
                        'cache_id' => $item->getKey(),
168
                        'cache_data' => $cacheData,
169
                        'cache_creation_date' => new Cassandra\Timestamp((new DateTime())->getTimestamp(), 0),
170
                        'cache_expiration_date' => new Cassandra\Timestamp($item->getExpirationDate()->getTimestamp(), 0),
171
                        'cache_length' => strlen($cacheData),
172
                    ],
173
                    'consistency' => Cassandra::CONSISTENCY_ALL,
174
                    'serial_consistency' => Cassandra::CONSISTENCY_SERIAL,
175
                ]
176
            );
177
178
            $query = sprintf(
179
                'INSERT INTO %s.%s
180
                    (
181
                      cache_uuid, 
182
                      cache_id, 
183
                      cache_data, 
184
                      cache_creation_date, 
185
                      cache_expiration_date,
186
                      cache_length
187
                    )
188
                  VALUES (:cache_uuid, :cache_id, :cache_data, :cache_creation_date, :cache_expiration_date, :cache_length);
189
            ',
190
                self::CASSANDRA_KEY_SPACE,
191
                self::CASSANDRA_TABLE
192
            );
193
194
            $result = $this->instance->execute(new Cassandra\SimpleStatement($query), $options);
195
            /**
196
             * There's no real way atm
197
             * to know if the item has
198
             * been really upserted
199
             */
200
            return $result instanceof Cassandra\Rows;
201
        } catch (InvalidArgumentException $e) {
202
            throw new PhpfastcacheInvalidArgumentException($e->getMessage(), 0, $e);
203
        }
204
    }
205
206
    /**
207
     * @param string $key
208
     * @param string $encodedKey
209
     * @return bool
210
     */
211
    protected function driverDelete(string $key, string $encodedKey): bool
212
    {
213
214
        try {
215
            $options = $this->getCompatibleExecutionOptionsArgument(
216
                [
217
                    'arguments' => [
218
                        'cache_id' => $key,
219
                    ],
220
                ]
221
            );
222
            $result = $this->instance->execute(
223
                new Cassandra\SimpleStatement(
224
                    sprintf(
225
                        'DELETE FROM %s.%s WHERE cache_id = :cache_id;',
226
                        self::CASSANDRA_KEY_SPACE,
227
                        self::CASSANDRA_TABLE
228
                    )
229
                ),
230
                $options
231
            );
232
233
            /**
234
             * There's no real way atm
235
             * to know if the item has
236
             * been really deleted
237
             */
238
            return $result instanceof Cassandra\Rows;
239
        } catch (Exception $e) {
240
            return false;
241
        }
242
    }
243
244
    /**
245
     * @return bool
246
     */
247
    protected function driverClear(): bool
248
    {
249
        try {
250
            $this->instance->execute(
251
                new Cassandra\SimpleStatement(
252
                    sprintf(
253
                        'TRUNCATE %s.%s;',
254
                        self::CASSANDRA_KEY_SPACE,
255
                        self::CASSANDRA_TABLE
256
                    )
257
                ),
258
                null
259
            );
260
261
            return true;
262
        } catch (Exception) {
263
            return false;
264
        }
265
    }
266
267
    /**
268
     * @return string
269
     */
270
    public function getHelp(): string
271
    {
272
        return <<<HELP
273
<p>
274
To install the php Cassandra extension via Pecl:
275
<code>sudo pecl install cassandra</code>
276
More information on: https://github.com/datastax/php-driver
277
Please note that this repository only provide php stubs and C/C++ sources, it does NOT provide php client.
278
</p>
279
HELP;
280
    }
281
282
    /**
283
     * @return DriverStatistic
284
     * @throws Exception
285
     */
286
    public function getStats(): DriverStatistic
287
    {
288
        $result = $this->instance->execute(
289
            new Cassandra\SimpleStatement(
290
                sprintf(
291
                    'SELECT SUM(cache_length) as cache_size FROM %s.%s',
292
                    self::CASSANDRA_KEY_SPACE,
293
                    self::CASSANDRA_TABLE
294
                )
295
            ),
296
            null
297
        );
298
299
        return (new DriverStatistic())
300
            ->setSize($result->first()['cache_size'])
301
            ->setRawData([])
302
            ->setData(implode(', ', array_keys($this->itemInstances)))
303
            ->setInfo('The cache size represents only the cache data itself without counting data structures associated to the cache entries.');
304
    }
305
306
    /**
307
     * @param array<string, mixed> $options
308
     * @return array<string, mixed>|Cassandra\ExecutionOptions
309
     */
310
    protected function getCompatibleExecutionOptionsArgument(array $options): mixed
311
    {
312
        if ($this->getConfig()->isUseLegacyExecutionOptions()) {
313
            return new Cassandra\ExecutionOptions($options);
314
        }
315
316
        return $options;
317
    }
318
}
319