Initial Kohana install
[speedfreak] / Server / system / libraries / drivers / Cache / Sqlite.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * SQLite-based Cache driver.
4  *
5  * $Id: Sqlite.php 4046 2009-03-05 19:23:29Z Shadowhand $
6  *
7  * @package    Cache
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Cache_Sqlite_Driver implements Cache_Driver {
13
14         // SQLite database instance
15         protected $db;
16
17         // Database error messages
18         protected $error;
19
20         /**
21          * Logs an SQLite error.
22          */
23         protected static function log_error($code)
24         {
25                 // Log an error
26                 Kohana::log('error', 'Cache: SQLite error: '.sqlite_error_string($error));
27         }
28
29         /**
30          * Tests that the storage location is a directory and is writable.
31          */
32         public function __construct($filename)
33         {
34                 // Get the directory name
35                 $directory = str_replace('\\', '/', realpath(pathinfo($filename, PATHINFO_DIRNAME))).'/';
36
37                 // Set the filename from the real directory path
38                 $filename = $directory.basename($filename);
39
40                 // Make sure the cache directory is writable
41                 if ( ! is_dir($directory) OR ! is_writable($directory))
42                         throw new Kohana_Exception('cache.unwritable', $directory);
43
44                 // Make sure the cache database is writable
45                 if (is_file($filename) AND ! is_writable($filename))
46                         throw new Kohana_Exception('cache.unwritable', $filename);
47
48                 // Open up an instance of the database
49                 $this->db = new SQLiteDatabase($filename, '0666', $error);
50
51                 // Throw an exception if there's an error
52                 if ( ! empty($error))
53                         throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error));
54
55                 $query  = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'caches'";
56                 $tables = $this->db->query($query, SQLITE_BOTH, $error);
57
58                 // Throw an exception if there's an error
59                 if ( ! empty($error))
60                         throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error));
61
62                 if ($tables->numRows() == 0)
63                 {
64                         Kohana::log('error', 'Cache: Initializing new SQLite cache database');
65
66                         // Issue a CREATE TABLE command
67                         $this->db->unbufferedQuery(Kohana::config('cache_sqlite.schema'));
68                 }
69         }
70
71         /**
72          * Checks if a cache id is already set.
73          *
74          * @param  string   cache id
75          * @return boolean
76          */
77         public function exists($id)
78         {
79                 // Find the id that matches
80                 $query = "SELECT id FROM caches WHERE id = '$id'";
81
82                 return ($this->db->query($query)->numRows() > 0);
83         }
84
85         /**
86          * Sets a cache item to the given data, tags, and lifetime.
87          *
88          * @param   string   cache id to set
89          * @param   string   data in the cache
90          * @param   array    cache tags
91          * @param   integer  lifetime
92          * @return  bool
93          */
94         public function set($id, $data, array $tags = NULL, $lifetime)
95         {
96                 // Serialize and escape the data
97                 $data = sqlite_escape_string(serialize($data));
98
99                 if ( ! empty($tags))
100                 {
101                         // Escape the tags, adding brackets so the tag can be explicitly matched
102                         $tags = sqlite_escape_string('<'.implode('>,<', $tags).'>');
103                 }
104
105                 // Cache Sqlite driver expects unix timestamp
106                 if ($lifetime !== 0)
107                 {
108                         $lifetime += time();
109                 }
110
111                 $query = $this->exists($id)
112                         ? "UPDATE caches SET tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'"
113                         : "INSERT INTO caches VALUES('$id', '$tags', '$lifetime', '$data')";
114
115                 // Run the query
116                 $this->db->unbufferedQuery($query, SQLITE_BOTH, $error);
117
118                 if ( ! empty($error))
119                 {
120                         self::log_error($error);
121                         return FALSE;
122                 }
123                 else
124                 {
125                         return TRUE;
126                 }
127         }
128
129         /**
130          * Finds an array of ids for a given tag.
131          *
132          * @param  string  tag name
133          * @return array   of ids that match the tag
134          */
135         public function find($tag)
136         {
137                 $query = "SELECT id,cache FROM caches WHERE tags LIKE '%<{$tag}>%'";
138                 $query = $this->db->query($query, SQLITE_BOTH, $error);
139
140                 // An array will always be returned
141                 $result = array();
142
143                 if ( ! empty($error))
144                 {
145                         self::log_error($error);
146                 }
147                 elseif ($query->numRows() > 0)
148                 {
149                         // Disable notices for unserializing
150                         $ER = error_reporting(~E_NOTICE);
151
152                         while ($row = $query->fetchObject())
153                         {
154                                 // Add each cache to the array
155                                 $result[$row->id] = unserialize($row->cache);
156                         }
157
158                         // Turn notices back on
159                         error_reporting($ER);
160                 }
161
162                 return $result;
163         }
164
165         /**
166          * Fetches a cache item. This will delete the item if it is expired or if
167          * the hash does not match the stored hash.
168          *
169          * @param  string  cache id
170          * @return mixed|NULL
171          */
172         public function get($id)
173         {
174                 $query = "SELECT id, expiration, cache FROM caches WHERE id = '$id' LIMIT 0, 1";
175                 $query = $this->db->query($query, SQLITE_BOTH, $error);
176
177                 if ( ! empty($error))
178                 {
179                         self::log_error($error);
180                 }
181                 elseif ($cache = $query->fetchObject())
182                 {
183                         // Make sure the expiration is valid and that the hash matches
184                         if ($cache->expiration != 0 AND $cache->expiration <= time())
185                         {
186                                 // Cache is not valid, delete it now
187                                 $this->delete($cache->id);
188                         }
189                         else
190                         {
191                                 // Disable notices for unserializing
192                                 $ER = error_reporting(~E_NOTICE);
193                                 
194                                 // Return the valid cache data
195                                 $data = $cache->cache;
196
197                                 // Turn notices back on
198                                 error_reporting($ER);
199                         }
200                 }
201
202                 // No valid cache found
203                 return NULL;
204         }
205
206         /**
207          * Deletes a cache item by id or tag
208          *
209          * @param  string  cache id or tag, or TRUE for "all items"
210          * @param  bool    delete a tag
211          * @return bool
212          */
213         public function delete($id, $tag = FALSE)
214         {
215                 if ($id === TRUE)
216                 {
217                         // Delete all caches
218                         $where = '1';
219                 }
220                 elseif ($tag === TRUE)
221                 {
222                         // Delete by tag
223                         $where = "tags LIKE '%<{$id}>%'";
224                 }
225                 else
226                 {
227                         // Delete by id
228                         $where = "id = '$id'";
229                 }
230
231                 $this->db->unbufferedQuery('DELETE FROM caches WHERE '.$where, SQLITE_BOTH, $error);
232
233                 if ( ! empty($error))
234                 {
235                         self::log_error($error);
236                         return FALSE;
237                 }
238                 else
239                 {
240                         return TRUE;
241                 }
242         }
243
244         /**
245          * Deletes all cache files that are older than the current time.
246          */
247         public function delete_expired()
248         {
249                 // Delete all expired caches
250                 $query = 'DELETE FROM caches WHERE expiration != 0 AND expiration <= '.time();
251
252                 $this->db->unbufferedQuery($query);
253
254                 return TRUE;
255         }
256
257 } // End Cache SQLite Driver