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:
100:
101: 102: 103: 104: 105:
106: public function clean(array $conditions)
107: {
108: if (!$this->pdo) {
109: $this->open();
110: }
111: if (!empty($conditions[Cache::ALL])) {
112: $this->pdo->exec('
113: BEGIN;
114: DELETE FROM tags;
115: DELETE FROM priorities;
116: COMMIT;
117: ');
118:
119: return NULL;
120: }
121:
122: $unions = $args = [];
123: if (!empty($conditions[Cache::TAGS])) {
124: $tags = (array) $conditions[Cache::TAGS];
125: $unions[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count($tags) - 1) . ')';
126: $args = $tags;
127: }
128:
129: if (!empty($conditions[Cache::PRIORITY])) {
130: $unions[] = 'SELECT DISTINCT key FROM priorities WHERE priority <= ?';
131: $args[] = (int) $conditions[Cache::PRIORITY];
132: }
133:
134: if (empty($unions)) {
135: return [];
136: }
137:
138: $unionSql = implode(' UNION ', $unions);
139:
140: $this->pdo->exec('BEGIN IMMEDIATE');
141:
142: $stmt = $this->pdo->prepare($unionSql);
143: $stmt->execute($args);
144: $keys = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
145:
146: if (empty($keys)) {
147: $this->pdo->exec('COMMIT');
148: return [];
149: }
150:
151: $this->pdo->prepare("DELETE FROM tags WHERE key IN ($unionSql)")->execute($args);
152: $this->pdo->prepare("DELETE FROM priorities WHERE key IN ($unionSql)")->execute($args);
153: $this->pdo->exec('COMMIT');
154:
155: return $keys;
156: }
157:
158: }
159: