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: public function write($key, array $dependencies)
68: {
69: if (!$this->pdo) {
70: $this->open();
71: }
72: $this->pdo->exec('BEGIN');
73:
74: if (!empty($dependencies[Cache::TAGS])) {
75: $this->pdo->prepare('DELETE FROM tags WHERE key = ?')->execute([$key]);
76:
77: foreach ((array) $dependencies[Cache::TAGS] as $tag) {
78: $arr[] = $key;
79: $arr[] = $tag;
80: }
81: $this->pdo->prepare('INSERT INTO tags (key, tag) SELECT ?, ?' . str_repeat('UNION SELECT ?, ?', count($arr) / 2 - 1))
82: ->execute($arr);
83: }
84:
85: if (!empty($dependencies[Cache::PRIORITY])) {
86: $this->pdo->prepare('REPLACE INTO priorities (key, priority) VALUES (?, ?)')
87: ->execute([$key, (int) $dependencies[Cache::PRIORITY]]);
88: }
89:
90: $this->pdo->exec('COMMIT');
91: }
92:
93:
94: public function clean(array $conditions)
95: {
96: if (!$this->pdo) {
97: $this->open();
98: }
99: if (!empty($conditions[Cache::ALL])) {
100: $this->pdo->exec('
101: BEGIN;
102: DELETE FROM tags;
103: DELETE FROM priorities;
104: COMMIT;
105: ');
106:
107: return null;
108: }
109:
110: $unions = $args = [];
111: if (!empty($conditions[Cache::TAGS])) {
112: $tags = (array) $conditions[Cache::TAGS];
113: $unions[] = 'SELECT DISTINCT key FROM tags WHERE tag IN (?' . str_repeat(', ?', count($tags) - 1) . ')';
114: $args = $tags;
115: }
116:
117: if (!empty($conditions[Cache::PRIORITY])) {
118: $unions[] = 'SELECT DISTINCT key FROM priorities WHERE priority <= ?';
119: $args[] = (int) $conditions[Cache::PRIORITY];
120: }
121:
122: if (empty($unions)) {
123: return [];
124: }
125:
126: $unionSql = implode(' UNION ', $unions);
127:
128: $this->pdo->exec('BEGIN IMMEDIATE');
129:
130: $stmt = $this->pdo->prepare($unionSql);
131: $stmt->execute($args);
132: $keys = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
133:
134: if (empty($keys)) {
135: $this->pdo->exec('COMMIT');
136: return [];
137: }
138:
139: $this->pdo->prepare("DELETE FROM tags WHERE key IN ($unionSql)")->execute($args);
140: $this->pdo->prepare("DELETE FROM priorities WHERE key IN ($unionSql)")->execute($args);
141: $this->pdo->exec('COMMIT');
142:
143: return $keys;
144: }
145: }
146: