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: $this->pdo = new \PDO('sqlite:' . $path);
28: $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
29: $this->pdo->exec('
30: PRAGMA foreign_keys = ON;
31: CREATE TABLE IF NOT EXISTS cache (
32: key BLOB NOT NULL PRIMARY KEY,
33: data BLOB NOT NULL,
34: expire INTEGER,
35: slide INTEGER
36: );
37: CREATE TABLE IF NOT EXISTS tags (
38: key BLOB NOT NULL REFERENCES cache ON DELETE CASCADE,
39: tag BLOB NOT NULL
40: );
41: CREATE INDEX IF NOT EXISTS cache_expire ON cache(expire);
42: CREATE INDEX IF NOT EXISTS tags_key ON tags(key);
43: CREATE INDEX IF NOT EXISTS tags_tag ON tags(tag);
44: PRAGMA synchronous = OFF;
45: ');
46: }
47:
48:
49: 50: 51: 52: 53:
54: public function read($key)
55: {
56: $stmt = $this->pdo->prepare('SELECT data, slide FROM cache WHERE key=? AND (expire IS NULL OR expire >= ?)');
57: $stmt->execute([$key, time()]);
58: if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
59: if ($row['slide'] !== NULL) {
60: $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key=?')->execute([time(), $key]);
61: }
62: return unserialize($row['data']);
63: }
64: }
65:
66:
67: 68: 69: 70: 71:
72: public function bulkRead(array $keys)
73: {
74: $stmt = $this->pdo->prepare('SELECT key, data, slide FROM cache WHERE key IN (?' . str_repeat(',?', count($keys) - 1) . ') AND (expire IS NULL OR expire >= ?)');
75: $stmt->execute(array_merge($keys, [time()]));
76: $result = [];
77: $updateSlide = [];
78: foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
79: if ($row['slide'] !== NULL) {
80: $updateSlide[] = $row['key'];
81: }
82: $result[$row['key']] = unserialize($row['data']);
83: }
84: if (!empty($updateSlide)) {
85: $stmt = $this->pdo->prepare('UPDATE cache SET expire = ? + slide WHERE key IN(?' . str_repeat(',?', count($updateSlide) - 1) . ')');
86: $stmt->execute(array_merge([time()], $updateSlide));
87: }
88: return $result;
89: }
90:
91:
92: 93: 94: 95: 96:
97: public function lock($key)
98: {
99: }
100:
101:
102: 103: 104: 105: 106: 107: 108:
109: public function write($key, $data, array $dependencies)
110: {
111: $expire = isset($dependencies[Cache::EXPIRATION]) ? $dependencies[Cache::EXPIRATION] + time() : NULL;
112: $slide = isset($dependencies[Cache::SLIDING]) ? $dependencies[Cache::EXPIRATION] : NULL;
113:
114: $this->pdo->exec('BEGIN TRANSACTION');
115: $this->pdo->prepare('REPLACE INTO cache (key, data, expire, slide) VALUES (?, ?, ?, ?)')
116: ->execute([$key, serialize($data), $expire, $slide]);
117:
118: if (!empty($dependencies[Cache::TAGS])) {
119: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
120: $arr[] = $key;
121: $arr[] = $tag;
122: }
123: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
124: ->execute($arr);
125: }
126: $this->pdo->exec('COMMIT');
127: }
128:
129:
130: 131: 132: 133: 134:
135: public function remove($key)
136: {
137: $this->pdo->prepare('DELETE FROM cache WHERE key=?')
138: ->execute([$key]);
139: }
140:
141:
142: 143: 144: 145: 146:
147: public function clean(array $conditions)
148: {
149: if (!empty($conditions[Cache::ALL])) {
150: $this->pdo->prepare('DELETE FROM cache')->execute();
151:
152: } else {
153: $sql = 'DELETE FROM cache WHERE expire < ?';
154: $args = [time()];
155:
156: if (!empty($conditions[Cache::TAGS])) {
157: $tags = (array) $conditions[Cache::TAGS];
158: $sql .= ' OR key IN (SELECT key FROM tags WHERE tag IN (?' . str_repeat(',?', count($tags) - 1) . '))';
159: $args = array_merge($args, $tags);
160: }
161:
162: $this->pdo->prepare($sql)->execute($args);
163: }
164: }
165:
166: }
167: