1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Caching\Storages;
9:
10: use Nette;
11: use Nette\Caching\Cache;
12:
13:
14: 15: 16:
17: class SQLiteStorage implements Nette\Caching\IStorage, Nette\Caching\IBulkReader
18: {
19: use Nette\SmartObject;
20:
21:
22: private $pdo;
23:
24:
25: public function __construct($path)
26: {
27: if ($path !== ':memory:' && !is_file($path)) {
28: touch($path);
29: }
30:
31: $this->pdo = new \PDO('sqlite:' . $path);
32: $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
33: $this->pdo->exec('
34: PRAGMA foreign_keys = ON;
35: CREATE TABLE IF NOT EXISTS cache (
36: key BLOB NOT NULL PRIMARY KEY,
37: data BLOB NOT NULL,
38: expire INTEGER,
39: slide INTEGER
40: );
41: CREATE TABLE IF NOT EXISTS tags (
42: key BLOB NOT NULL REFERENCES cache ON DELETE CASCADE,
43: tag BLOB NOT NULL
44: );
45: CREATE INDEX IF NOT EXISTS cache_expire ON cache(expire);
46: CREATE INDEX IF NOT EXISTS tags_key ON tags(key);
47: CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag);
48: PRAGMA synchronous = OFF;
49: ');
50: }
51:
52:
53: public function read($key)
54: {
55: $stmt = $this->pdo->prepare('SELECT data, slide FROM cache WHERE key=? AND (expire IS NULL OR expire >= ?)');
56: $stmt->execute([$key, time()]);
57: if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
58: if ($row['slide'] !== null) {
59: $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key=?')->execute([time(), $key]);
60: }
61: return unserialize($row['data']);
62: }
63: }
64:
65:
66: public function bulkRead(array $keys)
67: {
68: $stmt = $this->pdo->prepare('SELECT key, data, slide FROM cache WHERE key IN (?' . str_repeat(',?', count($keys) - 1) . ') AND (expire IS NULL OR expire >= ?)');
69: $stmt->execute(array_merge($keys, [time()]));
70: $result = [];
71: $updateSlide = [];
72: foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
73: if ($row['slide'] !== null) {
74: $updateSlide[] = $row['key'];
75: }
76: $result[$row['key']] = unserialize($row['data']);
77: }
78: if (!empty($updateSlide)) {
79: $stmt = $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key IN(?' . str_repeat(',?', count($updateSlide) - 1) . ')');
80: $stmt->execute(array_merge([time()], $updateSlide));
81: }
82: return $result;
83: }
84:
85:
86: public function lock($key)
87: {
88: }
89:
90:
91: public function write($key, $data, array $dependencies)
92: {
93: $expire = isset($dependencies[Cache::EXPIRATION]) ? $dependencies[Cache::EXPIRATION] + time() : null;
94: $slide = isset($dependencies[Cache::SLIDING]) ? $dependencies[Cache::EXPIRATION] : null;
95:
96: $this->pdo->exec('BEGIN TRANSACTION');
97: $this->pdo->prepare('REPLACE INTO cache (key, data, expire, slide) VALUES (?, ?, ?, ?)')
98: ->execute([$key, serialize($data), $expire, $slide]);
99:
100: if (!empty($dependencies[Cache::TAGS])) {
101: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
102: $arr[] = $key;
103: $arr[] = $tag;
104: }
105: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
106: ->execute($arr);
107: }
108: $this->pdo->exec('COMMIT');
109: }
110:
111:
112: public function remove($key)
113: {
114: $this->pdo->prepare('DELETE FROM cache WHERE key=?')
115: ->execute([$key]);
116: }
117:
118:
119: public function clean(array $conditions)
120: {
121: if (!empty($conditions[Cache::ALL])) {
122: $this->pdo->prepare('DELETE FROM cache')->execute();
123:
124: } else {
125: $sql = 'DELETE FROM cache WHERE expire < ?';
126: $args = [time()];
127:
128: if (!empty($conditions[Cache::TAGS])) {
129: $tags = (array) $conditions[Cache::TAGS];
130: $sql .= ' OR key IN (SELECT key FROM tags WHERE tag IN (?' . str_repeat(',?', count($tags) - 1) . '))';
131: $args = array_merge($args, $tags);
132: }
133:
134: $this->pdo->prepare($sql)->execute($args);
135: }
136: }
137: }
138: