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: 54: 55: 56: 57:
58: public function read($key)
59: {
60: $stmt = $this->pdo->prepare('SELECT data, slide FROM cache WHERE key=? AND (expire IS NULL OR expire >= ?)');
61: $stmt->execute([$key, time()]);
62: if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
63: if ($row['slide'] !== NULL) {
64: $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key=?')->execute([time(), $key]);
65: }
66: return unserialize($row['data']);
67: }
68: }
69:
70:
71: 72: 73: 74: 75:
76: public function bulkRead(array $keys)
77: {
78: $stmt = $this->pdo->prepare('SELECT key, data, slide FROM cache WHERE key IN (?' . str_repeat(',?', count($keys) - 1) . ') AND (expire IS NULL OR expire >= ?)');
79: $stmt->execute(array_merge($keys, [time()]));
80: $result = [];
81: $updateSlide = [];
82: foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
83: if ($row['slide'] !== NULL) {
84: $updateSlide[] = $row['key'];
85: }
86: $result[$row['key']] = unserialize($row['data']);
87: }
88: if (!empty($updateSlide)) {
89: $stmt = $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key IN(?' . str_repeat(',?', count($updateSlide) - 1) . ')');
90: $stmt->execute(array_merge([time()], $updateSlide));
91: }
92: return $result;
93: }
94:
95:
96: 97: 98: 99: 100:
101: public function lock($key)
102: {
103: }
104:
105:
106: 107: 108: 109: 110: 111: 112:
113: public function write($key, $data, array $dependencies)
114: {
115: $expire = isset($dependencies[Cache::EXPIRATION]) ? $dependencies[Cache::EXPIRATION] + time() : NULL;
116: $slide = isset($dependencies[Cache::SLIDING]) ? $dependencies[Cache::EXPIRATION] : NULL;
117:
118: $this->pdo->exec('BEGIN TRANSACTION');
119: $this->pdo->prepare('REPLACE INTO cache (key, data, expire, slide) VALUES (?, ?, ?, ?)')
120: ->execute([$key, serialize($data), $expire, $slide]);
121:
122: if (!empty($dependencies[Cache::TAGS])) {
123: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
124: $arr[] = $key;
125: $arr[] = $tag;
126: }
127: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
128: ->execute($arr);
129: }
130: $this->pdo->exec('COMMIT');
131: }
132:
133:
134: 135: 136: 137: 138:
139: public function remove($key)
140: {
141: $this->pdo->prepare('DELETE FROM cache WHERE key=?')
142: ->execute([$key]);
143: }
144:
145:
146: 147: 148: 149: 150:
151: public function clean(array $conditions)
152: {
153: if (!empty($conditions[Cache::ALL])) {
154: $this->pdo->prepare('DELETE FROM cache')->execute();
155:
156: } else {
157: $sql = 'DELETE FROM cache WHERE expire < ?';
158: $args = [time()];
159:
160: if (!empty($conditions[Cache::TAGS])) {
161: $tags = (array) $conditions[Cache::TAGS];
162: $sql .= ' OR key IN (SELECT key FROM tags WHERE tag IN (?' . str_repeat(',?', count($tags) - 1) . '))';
163: $args = array_merge($args, $tags);
164: }
165:
166: $this->pdo->prepare($sql)->execute($args);
167: }
168: }
169:
170: }
171: