1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* |
4
|
|
|
* This file is part of phpFastCache. |
5
|
|
|
* |
6
|
|
|
* @license MIT License (MIT) |
7
|
|
|
* |
8
|
|
|
* For full copyright and license information, please see the docs/CREDITS.txt file. |
9
|
|
|
* |
10
|
|
|
* @author Khoa Bui (khoaofgod) <[email protected]> http://www.phpfastcache.com |
11
|
|
|
* @author Georges.L (Geolim4) <[email protected]> |
12
|
|
|
* |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace phpFastCache\Drivers\Cassandra; |
16
|
|
|
|
17
|
|
|
use CouchbaseCluster as CouchbaseClient; |
18
|
|
|
use phpFastCache\Core\Pool\DriverBaseTrait; |
19
|
|
|
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface; |
20
|
|
|
use phpFastCache\Entities\driverStatistic; |
21
|
|
|
use phpFastCache\Exceptions\phpFastCacheDriverCheckException; |
22
|
|
|
use phpFastCache\Exceptions\phpFastCacheDriverException; |
23
|
|
|
use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException; |
24
|
|
|
use Psr\Cache\CacheItemInterface; |
25
|
|
|
use Cassandra; |
26
|
|
|
use Cassandra\Session as CassandraSession; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Class Driver |
30
|
|
|
* @package phpFastCache\Drivers |
31
|
|
|
* @property CassandraSession $instance Instance of driver service |
32
|
|
|
*/ |
33
|
|
|
class Driver implements ExtendedCacheItemPoolInterface |
34
|
|
|
{ |
35
|
|
|
const CASSANDRA_KEY_SPACE = 'phpfastcache'; |
36
|
|
|
const CASSANDRA_TABLE = 'cacheItems'; |
37
|
|
|
|
38
|
|
|
use DriverBaseTrait; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Driver constructor. |
42
|
|
|
* @param array $config |
43
|
|
|
* @throws phpFastCacheDriverException |
44
|
|
|
*/ |
45
|
|
View Code Duplication |
public function __construct(array $config = []) |
46
|
|
|
{ |
47
|
|
|
$this->setup($config); |
48
|
|
|
|
49
|
|
|
if (!$this->driverCheck()) { |
50
|
|
|
throw new phpFastCacheDriverCheckException(sprintf(self::DRIVER_CHECK_FAILURE, $this->getDriverName())); |
51
|
|
|
} else { |
52
|
|
|
$this->driverConnect(); |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @return bool |
58
|
|
|
*/ |
59
|
|
|
public function driverCheck() |
60
|
|
|
{ |
61
|
|
|
return extension_loaded('Cassandra') && class_exists('Cassandra'); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
66
|
|
|
* @return mixed |
67
|
|
|
* @throws phpFastCacheInvalidArgumentException |
68
|
|
|
*/ |
69
|
|
|
protected function driverWrite(CacheItemInterface $item) |
70
|
|
|
{ |
71
|
|
|
/** |
72
|
|
|
* Check for Cross-Driver type confusion |
73
|
|
|
*/ |
74
|
|
|
if ($item instanceof Item) { |
75
|
|
|
$cacheData = $this->encode($this->driverPreWrap($item)); |
76
|
|
|
$options = new Cassandra\ExecutionOptions([ |
77
|
|
|
'arguments' => [ |
78
|
|
|
'cache_uuid' => new Cassandra\Uuid(), |
79
|
|
|
'cache_id' => $item->getKey(), |
80
|
|
|
'cache_data' => $this->encode($this->driverPreWrap($item)), |
81
|
|
|
'cache_creation_date' => new Cassandra\Timestamp((new \DateTime())->getTimestamp()), |
82
|
|
|
'cache_expiration_date' => new Cassandra\Timestamp($item->getExpirationDate()->getTimestamp()), |
83
|
|
|
'cache_length' => strlen($cacheData) |
84
|
|
|
], |
85
|
|
|
'consistency' => Cassandra::CONSISTENCY_ALL, |
86
|
|
|
'serial_consistency' => Cassandra::CONSISTENCY_SERIAL |
87
|
|
|
]); |
88
|
|
|
|
89
|
|
|
$query = sprintf('INSERT INTO %s.%s |
90
|
|
|
( |
91
|
|
|
cache_uuid, |
92
|
|
|
cache_id, |
93
|
|
|
cache_data, |
94
|
|
|
cache_creation_date, |
95
|
|
|
cache_expiration_date, |
96
|
|
|
cache_length |
97
|
|
|
) |
98
|
|
|
VALUES (:cache_uuid, :cache_id, :cache_data, :cache_creation_date, :cache_expiration_date, :cache_length); |
99
|
|
|
', self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE); |
100
|
|
|
|
101
|
|
|
$result = $this->instance->execute(new Cassandra\SimpleStatement($query), $options); |
102
|
|
|
/** |
103
|
|
|
* There's no real way atm |
104
|
|
|
* to know if the item has |
105
|
|
|
* been really upserted |
106
|
|
|
*/ |
107
|
|
|
return $result instanceof Cassandra\Rows; |
|
|
|
|
108
|
|
|
} else { |
109
|
|
|
throw new phpFastCacheInvalidArgumentException('Cross-Driver type confusion detected'); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
115
|
|
|
* @return mixed |
116
|
|
|
*/ |
117
|
|
|
protected function driverRead(CacheItemInterface $item) |
118
|
|
|
{ |
119
|
|
|
try { |
120
|
|
|
$options = new Cassandra\ExecutionOptions([ |
121
|
|
|
'arguments' => ['cache_id' => $item->getKey()], |
122
|
|
|
'page_size' => 1 |
123
|
|
|
]); |
124
|
|
|
$query = sprintf( |
125
|
|
|
'SELECT cache_data FROM %s.%s WHERE cache_id = :cache_id;', |
126
|
|
|
self::CASSANDRA_KEY_SPACE, |
127
|
|
|
self::CASSANDRA_TABLE |
128
|
|
|
); |
129
|
|
|
$results = $this->instance->execute(new Cassandra\SimpleStatement($query), $options); |
130
|
|
|
|
131
|
|
|
if($results instanceof Cassandra\Rows && $results->count() === 1){ |
|
|
|
|
132
|
|
|
return $this->decode($results->first()['cache_data']); |
133
|
|
|
}else{ |
134
|
|
|
return null; |
135
|
|
|
} |
136
|
|
|
} catch (Cassandra\Exception $e) { |
|
|
|
|
137
|
|
|
return null; |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @param \Psr\Cache\CacheItemInterface $item |
143
|
|
|
* @return bool |
144
|
|
|
* @throws phpFastCacheInvalidArgumentException |
145
|
|
|
*/ |
146
|
|
|
protected function driverDelete(CacheItemInterface $item) |
147
|
|
|
{ |
148
|
|
|
/** |
149
|
|
|
* Check for Cross-Driver type confusion |
150
|
|
|
*/ |
151
|
|
|
if ($item instanceof Item) { |
152
|
|
|
try { |
153
|
|
|
$options = new Cassandra\ExecutionOptions([ |
154
|
|
|
'arguments' => [ |
155
|
|
|
'cache_id' => $item->getKey(), |
156
|
|
|
], |
157
|
|
|
]); |
158
|
|
|
$result = $this->instance->execute(new Cassandra\SimpleStatement(sprintf( |
159
|
|
|
'DELETE FROM %s.%s WHERE cache_id = :cache_id;', |
160
|
|
|
self::CASSANDRA_KEY_SPACE, |
161
|
|
|
self::CASSANDRA_TABLE |
162
|
|
|
)), $options); |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* There's no real way atm |
166
|
|
|
* to know if the item has |
167
|
|
|
* been really deleted |
168
|
|
|
*/ |
169
|
|
|
return $result instanceof Cassandra\Rows; |
|
|
|
|
170
|
|
|
} catch (Cassandra\Exception $e) { |
|
|
|
|
171
|
|
|
return false; |
172
|
|
|
} |
173
|
|
|
} else { |
174
|
|
|
throw new phpFastCacheInvalidArgumentException('Cross-Driver type confusion detected'); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @return bool |
180
|
|
|
*/ |
181
|
|
|
protected function driverClear() |
182
|
|
|
{ |
183
|
|
|
try { |
184
|
|
|
$result = $this->instance->execute(new Cassandra\SimpleStatement(sprintf( |
185
|
|
|
'TRUNCATE %s.%s;', |
186
|
|
|
self::CASSANDRA_KEY_SPACE, self::CASSANDRA_TABLE |
187
|
|
|
))); |
188
|
|
|
/** |
189
|
|
|
* There's no real way atm |
190
|
|
|
* to know if the item has |
191
|
|
|
* been really deleted |
192
|
|
|
*/ |
193
|
|
|
return $result instanceof Cassandra\Rows; |
|
|
|
|
194
|
|
|
} catch (Cassandra\Exception $e) { |
|
|
|
|
195
|
|
|
return false; |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @return bool |
201
|
|
|
*/ |
202
|
|
|
protected function driverConnect() |
203
|
|
|
{ |
204
|
|
|
if ($this->instance instanceof CouchbaseClient) { |
205
|
|
|
throw new \LogicException('Already connected to Couchbase server'); |
206
|
|
|
} else { |
207
|
|
|
$host = isset($this->config[ 'host' ]) ? $this->config[ 'host' ] : '127.0.0.1'; |
208
|
|
|
$port = isset($this->config[ 'port' ]) ? $this->config[ 'port' ] : 9042; |
209
|
|
|
$timeout = isset($this->config[ 'timeout' ]) ? $this->config[ 'timeout' ] : 2; |
210
|
|
|
$password = isset($this->config[ 'password' ]) ? $this->config[ 'password' ] : ''; |
211
|
|
|
$username = isset($this->config[ 'username' ]) ? $this->config[ 'username' ] : ''; |
212
|
|
|
|
213
|
|
|
$clusterBuilder = Cassandra::cluster() |
214
|
|
|
->withContactPoints($host) |
215
|
|
|
->withPort($port); |
216
|
|
|
|
217
|
|
|
if(!empty($this->config['ssl']['enabled'])){ |
218
|
|
|
if(!empty($this->config['ssl']['verify'])){ |
219
|
|
|
$sslBuilder = Cassandra::ssl()->withVerifyFlags(Cassandra::VERIFY_PEER_CERT); |
220
|
|
|
}else{ |
221
|
|
|
$sslBuilder = Cassandra::ssl()->withVerifyFlags(Cassandra::VERIFY_NONE); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
$clusterBuilder->withSSL($sslBuilder->build()); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
$clusterBuilder->withConnectTimeout($timeout); |
228
|
|
|
|
229
|
|
|
if($username){ |
230
|
|
|
$clusterBuilder->withCredentials($username, $password); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
$this->instance = $clusterBuilder->build()->connect(); |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* In case of emergency: |
237
|
|
|
* $this->instance->execute( |
238
|
|
|
* new Cassandra\SimpleStatement(sprintf("DROP KEYSPACE %s;", self::CASSANDRA_KEY_SPACE)) |
239
|
|
|
* ); |
240
|
|
|
*/ |
241
|
|
|
|
242
|
|
|
$this->instance->execute(new Cassandra\SimpleStatement(sprintf( |
243
|
|
|
"CREATE KEYSPACE IF NOT EXISTS %s WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };", |
244
|
|
|
self::CASSANDRA_KEY_SPACE |
245
|
|
|
))); |
246
|
|
|
$this->instance->execute(new Cassandra\SimpleStatement(sprintf('USE %s;', self::CASSANDRA_KEY_SPACE))); |
247
|
|
|
$this->instance->execute(new Cassandra\SimpleStatement(sprintf(' |
248
|
|
|
CREATE TABLE IF NOT EXISTS %s ( |
249
|
|
|
cache_uuid uuid, |
250
|
|
|
cache_id varchar, |
251
|
|
|
cache_data text, |
252
|
|
|
cache_creation_date timestamp, |
253
|
|
|
cache_expiration_date timestamp, |
254
|
|
|
cache_length int, |
255
|
|
|
PRIMARY KEY (cache_id) |
256
|
|
|
);', self::CASSANDRA_TABLE |
257
|
|
|
))); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return true; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/******************** |
264
|
|
|
* |
265
|
|
|
* PSR-6 Extended Methods |
266
|
|
|
* |
267
|
|
|
*******************/ |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* @return driverStatistic |
271
|
|
|
*/ |
272
|
|
|
public function getStats() |
273
|
|
|
{ |
274
|
|
|
$result = $this->instance->execute(new Cassandra\SimpleStatement(sprintf( |
275
|
|
|
'SELECT SUM(cache_length) as cache_size FROM %s.%s', |
276
|
|
|
self::CASSANDRA_KEY_SPACE, |
277
|
|
|
self::CASSANDRA_TABLE |
278
|
|
|
))); |
279
|
|
|
|
280
|
|
|
return (new driverStatistic()) |
281
|
|
|
->setSize($result->first()[ 'cache_size' ]) |
282
|
|
|
->setRawData([]) |
283
|
|
|
->setData(implode(', ', array_keys($this->itemInstances))) |
284
|
|
|
->setInfo('The cache size represents only the cache data itself without counting data structures associated to the cache entries.'); |
285
|
|
|
} |
286
|
|
|
} |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.