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: public function write($key, $data, array $dependencies)
113: {
114: $expire = isset($dependencies[Cache::EXPIRATION]) ? $dependencies[Cache::EXPIRATION] + time() : null;
115: $slide = isset($dependencies[Cache::SLIDING]) ? $dependencies[Cache::EXPIRATION] : null;
116:
117: $this->pdo->exec('BEGIN TRANSACTION');
118: $this->pdo->prepare('REPLACE INTO cache (key, data, expire, slide) VALUES (?, ?, ?, ?)')
119: ->execute([$key, serialize($data), $expire, $slide]);
120:
121: if (!empty($dependencies[Cache::TAGS])) {
122: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
123: $arr[] = $key;
124: $arr[] = $tag;
125: }
126: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
127: ->execute($arr);
128: }
129: $this->pdo->exec('COMMIT');
130: }
131:
132:
133: 134: 135: 136: 137:
138: public function remove($key)
139: {
140: $this->pdo->prepare('DELETE FROM cache WHERE key=?')
141: ->execute([$key]);
142: }
143:
144:
145: 146: 147: 148: 149:
150: public function clean(array $conditions)
151: {
152: if (!empty($conditions[Cache::ALL])) {
153: $this->pdo->prepare('DELETE FROM cache')->execute();
154:
155: } else {
156: $sql = 'DELETE FROM cache WHERE expire < ?';
157: $args = [time()];
158:
159: if (!empty($conditions[Cache::TAGS])) {
160: $tags = (array) $conditions[Cache::TAGS];
161: $sql .= ' OR key IN (SELECT key FROM tags WHERE tag IN (?' . str_repeat(',?', count($tags) - 1) . '))';
162: $args = array_merge($args, $tags);
163: }
164:
165: $this->pdo->prepare($sql)->execute($args);
166: }
167: }
168: }
169: