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 SQLiteJournal implements IJournal
18: {
19: use Nette\SmartObject;
20:
21:
22: private $path;
23:
24:
25: private $pdo;
26:
27:
28: 29: 30:
31: public function __construct($path)
32: {
33: $this->path = $path;
34: }
35:
36:
37: private function open()
38: {
39: if (!extension_loaded('pdo_sqlite')) {
40: throw new Nette\NotSupportedException('SQLiteJournal requires PHP extension pdo_sqlite which is not loaded.');
41: }
42:
43: if ($this->path !== ':memory:' && !is_file($this->path)) {
44: touch($this->path);
45: }
46:
47: $this->pdo = new \PDO('sqlite:' . $this->path);
48: $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
49: $this->pdo->exec('
50: PRAGMA foreign_keys = OFF;
51: PRAGMA journal_mode = WAL;
52: CREATE TABLE IF NOT EXISTS tags (
53: key BLOB NOT NULL,
54: tag BLOB NOT NULL
55: );
56: CREATE TABLE IF NOT EXISTS priorities (
57: key BLOB NOT NULL,
58: priority INT NOT NULL
59: );
60: CREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag);
61: CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_key_tag ON tags(key, tag);
62: CREATE UNIQUE INDEX IF NOT EXISTS idx_priorities_key ON priorities(key);
63: CREATE INDEX IF NOT EXISTS idx_priorities_priority ON priorities(priority);
64: ');
65: }
66:
67:
68: 69: 70: 71: 72: 73:
74: public function write($key, array $dependencies)
75: {
76: if (!$this->pdo) {
77: $this->open();
78: }
79: $this->pdo->exec('BEGIN');
80:
81: if (!empty($dependencies[Cache::TAGS])) {
82: $this->pdo->prepare('DELETE FROM tags WHERE key = ?')->execute([$key]);
83:
84: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
85: $arr[] = $key;
86: $arr[] = $tag;
87: }
88: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
89: ->execute($arr);
90: }
91:
92: if (!empty($dependencies[Cache::PRIORITY])) {
93: $this->pdo->prepare('REPLACE INTO priorities (key, priority) VALUES (?, ?)')
94: ->execute([$key, (int) $dependencies[Cache::PRIORITY]]);
95: }
96:
97: $this->pdo->exec('COMMIT');
98:
99: return TRUE;
100: }
101:
102:
103: 104: 105: 106: 107:
108: public function clean(array $conditions)
109: {
110: if (!$this->pdo) {
111: $this->open();
112: }
113: if (!empty($conditions[Cache::ALL])) {
114: $this->pdo->exec('
115: BEGIN;
116: DELETE FROM tags;
117: DELETE FROM priorities;
118: COMMIT;
119: ');
120:
121: return NULL;
122: }
123:
124: $unions = $args = [];
125: if (!empty($conditions[Cache::TAGS])) {
126: $tags = (array) $conditions[Cache::TAGS];
127: $unions[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count($tags) - 1) . ')';
128: $args = $tags;
129: }
130:
131: if (!empty($conditions[Cache::PRIORITY])) {
132: $unions[] = 'SELECT DISTINCT key FROM priorities WHERE priority <= ?';
133: $args[] = (int) $conditions[Cache::PRIORITY];
134: }
135:
136: if (empty($unions)) {
137: return [];
138: }
139:
140: $unionSql = implode(' UNION ', $unions);
141:
142: $this->pdo->exec('BEGIN IMMEDIATE');
143:
144: $stmt = $this->pdo->prepare($unionSql);
145: $stmt->execute($args);
146: $keys = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
147:
148: if (empty($keys)) {
149: $this->pdo->exec('COMMIT');
150: return [];
151: }
152:
153: $this->pdo->prepare("DELETE FROM tags WHERE key IN ($unionSql)")->execute($args);
154: $this->pdo->prepare("DELETE FROM priorities WHERE key IN ($unionSql)")->execute($args);
155: $this->pdo->exec('COMMIT');
156:
157: return $keys;
158: }
159:
160: }
161: