Passed
Push — master ( 94ecb9...a83314 )
by Fabio
11:05 queued 05:24
created

TDbCache   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 538
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 194
c 0
b 0
f 0
dl 0
loc 538
ccs 0
cts 249
cp 0
rs 2.8
wmc 70

28 Methods

Rating   Name   Duplication   Size   Complexity  
B initializeCache() 0 50 11
A doInitializeCache() 0 3 1
A doFlushCacheExpired() 0 3 1
B flushCacheExpired() 0 19 7
A init() 0 5 1
A getFlushInterval() 0 3 1
A setUsername() 0 3 1
A getConnectionID() 0 3 1
A getUsername() 0 3 1
A setConnectionID() 0 3 1
A setPassword() 0 3 1
A __destruct() 0 6 2
A getValue() 0 13 3
A setFlushInterval() 0 3 1
A fxGetCronTaskInfos() 0 3 1
A getPassword() 0 3 1
A setConnectionString() 0 3 1
A createDbConnection() 0 25 6
A setCacheTableName() 0 3 1
A getConnectionString() 0 3 1
A getDbConnection() 0 8 2
A getCacheTableName() 0 3 1
A setAutoCreateCacheTable() 0 3 1
A getAutoCreateCacheTable() 0 3 1
A addValue() 0 21 5
A flush() 0 18 4
A deleteValue() 0 15 3
B setValue() 0 52 9

How to fix   Complexity   

Complex Class

Complex classes like TDbCache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TDbCache, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TDbCache class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Caching;
11
12
use Prado\Prado;
13
use Prado\Data\TDataSourceConfig;
14
use Prado\Data\TDbConnection;
15
use Prado\Exceptions\TConfigurationException;
16
use Prado\TPropertyValue;
17
use Prado\Util\Cron\TCronTaskInfo;
18
19
/**
20
 * TDbCache class
21
 *
22
 * TDbCache implements a cache application module by storing cached data in a database.
23
 *
24
 * TDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to retrieve
25
 * data from databases. In order to use TDbCache, you need to enable the PDO extension
26
 * as well as the corresponding PDO DB driver. For example, to use SQLite database
27
 * to store cached data, you need both php_pdo and php_pdo_sqlite extensions.
28
 *
29
 * By default, TDbCache creates and uses an SQLite database under the application
30
 * runtime directory. You may change this default setting by specifying the following
31
 * properties:
32
 * - {@link setConnectionID ConnectionID} or
33
 * - {@link setConnectionString ConnectionString}, {@link setUsername Username} and {@link setPassword Pasword}.
34
 *
35
 * The cached data is stored in a table in the specified database.
36
 * By default, the name of the table is called 'pradocache'. If the table does not
37
 * exist in the database, it will be automatically created with the following structure:
38
 * <code>
39
 * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT)
40
 * CREATE INDEX IX_itemkey ON pradocache (itemkey)
41
 * CREATE INDEX IX_expire ON pradocache (expire)
42
 * </code>
43
 *
44
 * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable
45
 * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.)
46
 *
47
 * Important: Make sure that the indices are non-unique!
48
 *
49
 * If you want to change the cache table name, or if you want to create the table by yourself,
50
 * you may set {@link setCacheTableName CacheTableName} and {@link setAutoCreateCacheTable AutoCreateCacheTableName} properties.
51
 *
52
 * {@link setFlushInterval FlushInterval} control how often expired items will be removed from cache.
53
 * If you prefer to remove expired items manualy e.g. via cronjob you can disable automatic deletion by setting FlushInterval to '0'.
54
 *
55
 * The following basic cache operations are implemented:
56
 * - {@link get} : retrieve the value with a key (if any) from cache
57
 * - {@link set} : store the value with a key into cache
58
 * - {@link add} : store the value only if cache does not have this key
59
 * - {@link delete} : delete the value with the specified key from cache
60
 * - {@link flush} : delete all values from cache
61
 *
62
 * Each value is associated with an expiration time. The {@link get} operation
63
 * ensures that any expired value will not be returned. The expiration time by
64
 * the number of seconds. A expiration time 0 represents never expire.
65
 *
66
 * By definition, cache does not ensure the existence of a value
67
 * even if it never expires. Cache is not meant to be an persistent storage.
68
 *
69
 * Do not use the same database file for multiple applications using TDbCache.
70
 * Also note, cache is shared by all user sessions of an application.
71
 *
72
 * Some usage examples of TDbCache are as follows,
73
 * <code>
74
 * $cache=new TDbCache;  // TDbCache may also be loaded as a Prado application module
75
 * $cache->init(null);
76
 * $cache->add('object',$object);
77
 * $object2=$cache->get('object');
78
 * </code>
79
 *
80
 * If loaded, TDbCache will register itself with {@link TApplication} as the
81
 * cache module. It can be accessed via {@link TApplication::getCache()}.
82
 *
83
 * TDbCache may be configured in application configuration file as follows
84
 * <code>
85
 * <module id="cache" class="Prado\Caching\TDbCache" />
86
 * </code>
87
 *
88
 * @author Qiang Xue <[email protected]>
89
 * @since 3.1.0
90
 */
91
class TDbCache extends TCache implements \Prado\Util\IDbModule
92
{
93
	/**
94
	 * @var string the ID of TDataSourceConfig module
95
	 */
96
	private $_connID = '';
97
	/**
98
	 * @var TDbConnection the DB connection instance
99
	 */
100
	private $_db;
101
	/**
102
	 * @var string name of the DB cache table
103
	 */
104
	private $_cacheTable = 'pradocache';
105
	/**
106
	 * @var int Interval expired items will be removed from cache
107
	 */
108
	private $_flushInterval = 60;
109
	/**
110
	 * @var bool
111
	 */
112
	private $_cacheInitialized = false;
113
	/**
114
	 * @var bool
115
	 */
116
	private $_createCheck = false;
117
	/**
118
	 * @var bool whether the cache DB table should be created automatically
119
	 */
120
	private $_autoCreate = true;
121
	private $_username = '';
122
	private $_password = '';
123
	private $_connectionString = '';
124
125
	/**
126
	 * Destructor.
127
	 * Disconnect the db connection.
128
	 */
129
	public function __destruct()
130
	{
131
		if ($this->_db !== null) {
132
			$this->_db->setActive(false);
133
		}
134
		parent::__destruct();
135
	}
136
137
	/**
138
	 * Initializes this module.
139
	 * This method is required by the IModule interface.
140
	 * attach {@link doInitializeCache} to TApplication.OnLoadStateComplete event
141
	 * attach {@link doFlushCacheExpired} to TApplication.OnSaveState event
142
	 * @param \Prado\Xml\TXmlElement $config configuration for this module, can be null
143
	 */
144
	public function init($config)
145
	{
146
		$this->getApplication()->attachEventHandler('OnLoadStateComplete', [$this, 'doInitializeCache']);
147
		$this->getApplication()->attachEventHandler('OnSaveState', [$this, 'doFlushCacheExpired']);
148
		parent::init($config);
149
	}
150
151
	/**
152
	 * Event listener for TApplication.OnSaveState
153
	 * @since 3.1.5
154
	 * @see flushCacheExpired
155
	 */
156
	public function doFlushCacheExpired()
157
	{
158
		$this->flushCacheExpired(false);
159
	}
160
161
	/**
162
	 * Event listener for TApplication.OnLoadStateComplete
163
	 *
164
	 * @since 3.1.5
165
	 * @see initializeCache
166
	 */
167
	public function doInitializeCache()
168
	{
169
		$this->initializeCache();
170
	}
171
172
	/**
173
	 * Initialize TDbCache
174
	 *
175
	 * If {@link setAutoCreateCacheTable AutoCreateCacheTableName} is 'true' check existence of cache table
176
	 * and create table if does not exist.
177
	 *
178
	 * @param bool $force Force override global state check
179
	 * @throws TConfigurationException if any error happens during creating database or cache table.
180
	 * @since 3.1.5
181
	 */
182
	protected function initializeCache($force = false)
183
	{
184
		if ($this->_cacheInitialized && !$force) {
185
			return;
186
		}
187
		$db = $this->getDbConnection();
188
		try {
189
			$key = 'TDbCache:' . $this->_cacheTable . ':created';
190
			if ($force) {
191
				$this->_createCheck = false;
192
			} else {
193
				$this->_createCheck = $this->getApplication()->getGlobalState($key, 0);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getApplication()->getGlobalState($key, 0) can also be of type integer. However, the property $_createCheck is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
194
			}
195
196
			if ($this->_autoCreate && !$this->_createCheck) {
197
				Prado::trace(($force ? 'Force initializing: ' : 'Initializing: ') . $this->_connID . ', ' . $this->_cacheTable, TDbCache::class);
198
199
				$sql = 'SELECT 1 FROM ' . $this->_cacheTable . ' WHERE 0=1';
200
				$db->createCommand($sql)->queryScalar();
201
202
				$this->_createCheck = true;
203
				$this->getApplication()->setGlobalState($key, time());
204
			}
205
		} catch (\Exception $e) {
206
			// DB table not exists
207
			if ($this->_autoCreate) {
208
				Prado::trace('Autocreate: ' . $this->_cacheTable, TDbCache::class);
209
210
				$driver = $db->getDriverName();
211
				if ($driver === 'mysql') {
212
					$blob = 'LONGBLOB';
213
				} elseif ($driver === 'pgsql') {
214
					$blob = 'BYTEA';
215
				} else {
216
					$blob = 'BLOB';
217
				}
218
219
				$sql = 'CREATE TABLE ' . $this->_cacheTable . " (itemkey CHAR(128) PRIMARY KEY, value $blob, expire INTEGER)";
220
				$db->createCommand($sql)->execute();
221
222
				$sql = 'CREATE INDEX IX_expire ON ' . $this->_cacheTable . ' (expire)';
223
				$db->createCommand($sql)->execute();
224
225
				$this->_createCheck = true;
226
				$this->getApplication()->setGlobalState($key, time());
227
			} else {
228
				throw new TConfigurationException('db_cachetable_inexistent', $this->_cacheTable);
229
			}
230
		}
231
		$this->_cacheInitialized = true;
232
	}
233
234
	/**
235
	 * Flush expired values from cache depending on {@link setFlushInterval FlushInterval}
236
	 * @param bool $force override {@link setFlushInterval FlushInterval} and force deletion of expired items
237
	 * @since 3.1.5
238
	 */
239
	public function flushCacheExpired($force = false)
240
	{
241
		$interval = $this->getFlushInterval();
242
		if (!$force && $interval === 0) {
243
			return;
244
		}
245
246
		$key = 'TDbCache:' . $this->_cacheTable . ':flushed';
247
		$now = time();
248
		$next = $interval + (int) $this->getApplication()->getGlobalState($key, 0);
249
250
		if ($force || $next <= $now) {
251
			if (!$this->_cacheInitialized) {
252
				$this->initializeCache();
253
			}
254
			Prado::trace(($force ? 'Force flush of expired items: ' : 'Flush expired items: ') . $this->_connID . ', ' . $this->_cacheTable, TDbCache::class);
255
			$sql = 'DELETE FROM ' . $this->_cacheTable . ' WHERE expire<>0 AND expire<' . $now;
256
			$this->getDbConnection()->createCommand($sql)->execute();
257
			$this->getApplication()->setGlobalState($key, $now);
258
		}
259
	}
260
261
	/**
262
	 * @param object $sender the object raising fxGetCronTaskInfos.
263
	 * @param mixed $param the parameter
264
	 * @since 4.2.0
265
	 */
266
	public function fxGetCronTaskInfos($sender, $param)
0 ignored issues
show
Unused Code introduced by
The parameter $sender is not used and could be removed. ( Ignorable by Annotation )

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

266
	public function fxGetCronTaskInfos(/** @scrutinizer ignore-unused */ $sender, $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $param is not used and could be removed. ( Ignorable by Annotation )

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

266
	public function fxGetCronTaskInfos($sender, /** @scrutinizer ignore-unused */ $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
267
	{
268
		return new TCronTaskInfo('dbcacheflushexpired', $this->getId() . '->flushCacheExpired(true)', $this, Prado::localize('DbCache Flush Expired Keys'), Prado::localize('This manually clears out the expired keys of TDbCache.'));
269
	}
270
271
	/**
272
	 * @return int Interval in sec expired items will be removed from cache. Default to 60
273
	 * @since 3.1.5
274
	 */
275
	public function getFlushInterval()
276
	{
277
		return $this->_flushInterval;
278
	}
279
280
	/**
281
	 * Sets interval expired items will be removed from cache
282
	 *
283
	 * To disable automatic deletion of expired items,
284
	 * e.g. for external flushing via cron you can set value to '0'
285
	 *
286
	 * @param int $value Interval in sec
287
	 * @since 3.1.5
288
	 */
289
	public function setFlushInterval($value)
290
	{
291
		$this->_flushInterval = (int) $value;
292
	}
293
294
	/**
295
	 * Creates the DB connection.
296
	 * @throws TConfigurationException if module ID is invalid or empty
297
	 * @return \Prado\Data\TDbConnection the created DB connection
298
	 */
299
	protected function createDbConnection()
300
	{
301
		if ($this->_connID !== '') {
302
			$config = $this->getApplication()->getModule($this->_connID);
303
			if ($config instanceof TDataSourceConfig) {
304
				return $config->getDbConnection();
305
			} else {
306
				throw new TConfigurationException('dbcache_connectionid_invalid', $this->_connID);
307
			}
308
		} else {
309
			$db = new TDbConnection();
310
			if ($this->_connectionString !== '') {
311
				$db->setConnectionString($this->_connectionString);
312
				if ($this->_username !== '') {
313
					$db->setUsername($this->_username);
314
				}
315
				if ($this->_password !== '') {
316
					$db->setPassword($this->_password);
317
				}
318
			} else {
319
				// default to SQLite3 database
320
				$dbFile = $this->getApplication()->getRuntimePath() . '/sqlite3.cache';
321
				$db->setConnectionString('sqlite:' . $dbFile);
322
			}
323
			return $db;
324
		}
325
	}
326
327
	/**
328
	 * @return \Prado\Data\TDbConnection the DB connection instance
329
	 */
330
	public function getDbConnection()
331
	{
332
		if ($this->_db === null) {
333
			$this->_db = $this->createDbConnection();
334
		}
335
336
		$this->_db->setActive(true);
337
		return $this->_db;
338
	}
339
340
	/**
341
	 * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set.
342
	 * @since 3.1.1
343
	 */
344
	public function getConnectionID()
345
	{
346
		return $this->_connID;
347
	}
348
349
	/**
350
	 * Sets the ID of a TDataSourceConfig module.
351
	 * The datasource module will be used to establish the DB connection for this cache module.
352
	 * The database connection can also be specified via {@link setConnectionString ConnectionString}.
353
	 * When both ConnectionID and ConnectionString are specified, the former takes precedence.
354
	 * @param string $value ID of the {@link TDataSourceConfig} module
355
	 * @since 3.1.1
356
	 */
357
	public function setConnectionID($value)
358
	{
359
		$this->_connID = $value;
360
	}
361
362
	/**
363
	 * @return string The Data Source Name, or DSN, contains the information required to connect to the database.
364
	 */
365
	public function getConnectionString()
366
	{
367
		return $this->_connectionString;
368
	}
369
370
	/**
371
	 * @param string $value The Data Source Name, or DSN, contains the information required to connect to the database.
372
	 * @see http://www.php.net/manual/en/function.pdo-construct.php
373
	 */
374
	public function setConnectionString($value)
375
	{
376
		$this->_connectionString = $value;
377
	}
378
379
	/**
380
	 * @return string the username for establishing DB connection. Defaults to empty string.
381
	 */
382
	public function getUsername()
383
	{
384
		return $this->_username;
385
	}
386
387
	/**
388
	 * @param string $value the username for establishing DB connection
389
	 */
390
	public function setUsername($value)
391
	{
392
		$this->_username = $value;
393
	}
394
395
	/**
396
	 * @return string the password for establishing DB connection. Defaults to empty string.
397
	 */
398
	public function getPassword()
399
	{
400
		return $this->_password;
401
	}
402
403
	/**
404
	 * @param string $value the password for establishing DB connection
405
	 */
406
	public function setPassword(#[\SensitiveParameter] $value)
407
	{
408
		$this->_password = $value;
409
	}
410
411
	/**
412
	 * @return string the name of the DB table to store cache content. Defaults to 'pradocache'.
413
	 * @see setAutoCreateCacheTable
414
	 */
415
	public function getCacheTableName()
416
	{
417
		return $this->_cacheTable;
418
	}
419
420
	/**
421
	 * Sets the name of the DB table to store cache content.
422
	 * Note, if {@link setAutoCreateCacheTable AutoCreateCacheTable} is false
423
	 * and you want to create the DB table manually by yourself,
424
	 * you need to make sure the DB table is of the following structure:
425
	 * <code>
426
	 * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT)
427
	 * CREATE INDEX IX_itemkey ON pradocache (itemkey)
428
	 * CREATE INDEX IX_expire ON pradocache (expire)
429
	 * </code>
430
	 *
431
	 * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable
432
	 * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.)
433
	 *
434
	 * Important: Make sure that the indices are non-unique!
435
	 *
436
	 * @param string $value the name of the DB table to store cache content
437
	 * @see setAutoCreateCacheTable
438
	 */
439
	public function setCacheTableName($value)
440
	{
441
		$this->_cacheTable = $value;
442
	}
443
444
	/**
445
	 * @return bool whether the cache DB table should be automatically created if not exists. Defaults to true.
446
	 * @see setAutoCreateCacheTable
447
	 */
448
	public function getAutoCreateCacheTable()
449
	{
450
		return $this->_autoCreate;
451
	}
452
453
	/**
454
	 * @param bool $value whether the cache DB table should be automatically created if not exists.
455
	 * @see setCacheTableName
456
	 */
457
	public function setAutoCreateCacheTable($value)
458
	{
459
		$this->_autoCreate = TPropertyValue::ensureBoolean($value);
460
	}
461
462
	/**
463
	 * Retrieves a value from cache with a specified key.
464
	 * This is the implementation of the method declared in the parent class.
465
	 * @param string $key a unique key identifying the cached value
466
	 * @return false|string the value stored in cache, false if the value is not in the cache or expired.
467
	 */
468
	protected function getValue($key)
469
	{
470
		if (!$this->_cacheInitialized) {
471
			$this->initializeCache();
472
		}
473
474
		$sql = 'SELECT value FROM ' . $this->_cacheTable . ' WHERE itemkey=\'' . $key . '\' AND (expire=0 OR expire>' . time() . ') ORDER BY expire DESC';
475
		$command = $this->getDbConnection()->createCommand($sql);
476
		try {
477
			return unserialize($command->queryScalar());
478
		} catch (\Exception $e) {
479
			$this->initializeCache(true);
480
			return unserialize($command->queryScalar());
481
		}
482
	}
483
484
	/**
485
	 * Stores a value identified by a key in cache.
486
	 * This is the implementation of the method declared in the parent class.
487
	 *
488
	 * @param string $key the key identifying the value to be cached
489
	 * @param string $value the value to be cached
490
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
491
	 * @return bool true if the value is successfully stored into cache, false otherwise
492
	 */
493
	protected function setValue($key, $value, $expire)
494
	{
495
		if (!$this->_cacheInitialized) {
496
			$this->initializeCache();
497
		}
498
		$db = $this->getDbConnection();
499
		$driver = $db->getDriverName();
500
		if (in_array($driver, ['mysql', 'mysqli', 'sqlite', 'ibm', 'oci', 'sqlsrv', 'mssql', 'dblib', 'pgsql'])) {
501
			$expire = ($expire <= 0) ? 0 : time() + $expire;
502
			if (in_array($driver, ['mysql', 'mysqli', 'sqlite'])) {
503
				$sql = "REPLACE INTO {$this->_cacheTable} (itemkey,value,expire) VALUES (:key,:value,$expire)";
504
			} elseif ($driver === 'pgsql') {
505
				$sql = "INSERT INTO {$this->_cacheTable} (itemkey, value, expire) VALUES (:key, :value, :expire) " .
506
					"ON CONFLICT (itemkey) DO UPDATE SET value = EXCLUDED.value, expire = EXCLUDED.expire";
507
			} else {
508
				$sql = "MERGE INTO {$this->_cacheTable} AS c " .
509
				"USING (SELECT :key AS itemkey, :value AS value, $expire AS expire) AS data " .
510
				"ON c.itemkey = data.itemkey " .
511
				"WHEN MATCHED THEN " .
512
					"UPDATE SET c.value = data.value, c.expire = data.expire " .
513
				"WHEN NOT MATCHED THEN " .
514
					"INSERT (itemkey, value, expire) " .
515
					"VALUES (data.itemkey, data.value, data.expire)";
516
			}
517
			$command = $db->createCommand($sql);
518
			$command->bindValue(':key', $key, \PDO::PARAM_STR);
519
			$command->bindValue(':value', serialize($value), \PDO::PARAM_LOB);
520
521
			try {
522
				$command->execute();
523
				return true;
524
			} catch (\Exception $e) {
525
				try {
526
					$this->initializeCache(true);
527
					$command->execute();
528
					return true;
529
				} catch (\Exception $e) {
530
					return false;
531
				}
532
			}
533
		} else {
534
			$isCurrentTransaction = $this->getDbConnection()->getCurrentTransaction();
535
			$transaction = $this->getDbConnection()->getCurrentTransaction() ?? $this->getDbConnection()->beginTransaction();
536
537
			$this->deleteValue($key);
538
			$return = $this->addValue($key, $value, $expire);
539
540
			if (!$isCurrentTransaction) {
0 ignored issues
show
introduced by
$isCurrentTransaction is of type Prado\Data\TDbTransaction, thus it always evaluated to true.
Loading history...
541
				$transaction->commit();
542
			}
543
544
			return $return;
545
		}
546
	}
547
548
	/**
549
	 * Stores a value identified by a key into cache if the cache does not contain this key.
550
	 * This is the implementation of the method declared in the parent class.
551
	 *
552
	 * @param string $key the key identifying the value to be cached
553
	 * @param string $value the value to be cached
554
	 * @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
555
	 * @return bool true if the value is successfully stored into cache, false otherwise
556
	 */
557
	protected function addValue($key, $value, $expire)
558
	{
559
		if (!$this->_cacheInitialized) {
560
			$this->initializeCache();
561
		}
562
		$expire = ($expire <= 0) ? 0 : time() + $expire;
563
		$sql = "INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)";
564
		$command = $this->getDbConnection()->createCommand($sql);
565
		$command->bindValue(':key', $key, \PDO::PARAM_STR);
566
		$command->bindValue(':value', serialize($value), \PDO::PARAM_LOB);
567
568
		try {
569
			$command->execute();
570
			return true;
571
		} catch (\Exception $e) {
572
			try {
573
				$this->initializeCache(true);
574
				$command->execute();
575
				return true;
576
			} catch (\Exception $e) {
577
				return false;
578
			}
579
		}
580
	}
581
582
	/**
583
	 * Deletes a value with the specified key from cache
584
	 * This is the implementation of the method declared in the parent class.
585
	 * @param string $key the key of the value to be deleted
586
	 * @return bool if no error happens during deletion
587
	 */
588
	protected function deleteValue($key)
589
	{
590
		if (!$this->_cacheInitialized) {
591
			$this->initializeCache();
592
		}
593
594
		$command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key");
595
		$command->bindValue(':key', $key, \PDO::PARAM_STR);
596
		try {
597
			$command->execute();
598
			return true;
599
		} catch (\Exception $e) {
600
			$this->initializeCache(true);
601
			$command->execute();
602
			return true;
603
		}
604
	}
605
606
	/**
607
	 * Deletes all values from cache.
608
	 * Be careful of performing this operation if the cache is shared by multiple applications.
609
	 * @return bool if no error happens during flush
610
	 */
611
	public function flush()
612
	{
613
		if (!$this->_cacheInitialized) {
614
			$this->initializeCache();
615
		}
616
		$command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable}");
617
		try {
618
			$command->execute();
619
		} catch (\Exception $e) {
620
			try {
621
				$this->initializeCache(true);
622
				$command->execute();
623
				return true;
624
			} catch (\Exception $e) {
625
				return false;
626
			}
627
		}
628
		return true;
629
	}
630
}
631