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