Development

/branches/1.0/lib/cache/sfSQLiteCache.class.php

You must first sign up to be able to contribute.

root/branches/1.0/lib/cache/sfSQLiteCache.class.php

Revision 3935, 8.1 kB (checked in by fabien, 7 years ago)

fixed sfSQLiteCache class

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * Cache class that stores content in a sqlite database.
13  *
14  * @package    symfony
15  * @subpackage cache
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfSQLiteCache extends sfCache
20 {
21   const DEFAULT_NAMESPACE = '';
22
23   protected $conn = null;
24
25  /**
26   * File where to put the cache database (or :memory: to store cache in memory)
27   */
28   protected $database = '';
29
30  /**
31   * Disable / Tune the automatic cleaning process
32   *
33   * The automatic cleaning process destroy too old (for the given life time)
34   * cache files when a new cache file is written.
35   * 0               => no automatic cache cleaning
36   * 1               => systematic cache cleaning
37   * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
38   */
39   protected $automaticCleaningFactor = 500;
40
41  /**
42   * Constructor.
43   *
44   * @param string The database name
45   */
46   public function __construct($database = null)
47   {
48     if (!extension_loaded('sqlite'))
49     {
50       throw new sfConfigurationException('sfSQLiteCache class needs "sqlite" extension');
51     }
52
53     $this->setDatabase($database);
54   }
55
56  /**
57    * Initializes the cache.
58    *
59    * @param array An array of options
60    * Available options:
61    *  - database:                database name
62    *  - automaticCleaningFactor: disable / tune automatic cleaning process (int)
63    *
64    */
65   public function initialize($options = array())
66   {
67     if (isset($options['database']))
68     {
69       $this->setDatabase($options['database']);
70       unset($options['database']);
71     }
72
73     $availableOptions = array('automaticCleaningFactor');
74     foreach ($options as $key => $value)
75     {
76       if (!in_array($key, $availableOptions) && sfConfig::get('sf_logging_enabled'))
77       {
78         sfLogger::getInstance()->err(sprintf('sfSQLiteCache cannot take "%s" as an option', $key));
79       }
80
81       $this->$key = $value;
82     }
83   }
84
85  /**
86    * Sets the database name.
87    *
88    * @param string The database name where to store the cache
89    */
90   public function setDatabase($database)
91   {
92     if (!$database)
93     {
94       return;
95     }
96
97     $this->database = $database;
98
99     $new = false;
100     if (':memory:' == $database)
101     {
102       $new = true;
103     }
104     elseif (!is_file($database))
105     {
106       $new = true;
107
108       // create cache dir if needed
109       $dir = dirname($database);
110       $current_umask = umask(0000);
111       if (!is_dir($dir))
112       {
113         @mkdir($dir, 0777, true);
114       }
115
116       touch($database);
117       umask($current_umask);
118     }
119
120     if (!($this->conn = @sqlite_open($this->database, 0644, $errmsg)))
121     {
122       throw new sfException(sprintf("Unable to connect to SQLite database: %s", $errmsg));
123     }
124
125     if ($new)
126     {
127       $this->createSchema();
128     }
129   }
130
131  /**
132    * Creates the database schema.
133    *
134    * @throws sfException
135    */
136   protected function createSchema()
137   {
138     $statements = array(
139       "CREATE TABLE [cache] (
140         [id] VARCHAR(255),
141         [namespace] VARCHAR(255),
142         [data] LONGVARCHAR,
143         [created_at] TIMESTAMP
144       )",
145       "CREATE UNIQUE INDEX [cache_unique] ON [cache] ([namespace], [id])",
146     );
147
148     foreach ($statements as $statement)
149     {
150       if (!sqlite_query($statement, $this->conn))
151       {
152         throw new sfException(sqlite_error_string(sqlite_last_error($this->database)));
153       }
154     }
155   }
156
157  /**
158    * Destructor.
159    */
160   public function __destruct()
161   {
162     sqlite_close($this->conn);
163   }
164
165  /**
166    * Gets the database name.
167    *
168    * @return string The database name
169    */
170   public function getDatabase()
171   {
172     return $this->database;
173   }
174
175  /**
176   * Tests if a cache is available and (if yes) returns it.
177   *
178   * @param  string  The cache id
179   * @param  string  The name of the cache namespace
180   * @param  boolean If set to true, the cache validity won't be tested
181   *
182   * @return string  The data in the cache (or null if no cache available)
183   *
184   * @see sfCache
185   */
186   public function get($id, $namespace = self::DEFAULT_NAMESPACE, $doNotTestCacheValidity = false)
187   {
188     $statement = sprintf("SELECT data FROM cache WHERE id = '%s' AND namespace = '%s'", sqlite_escape_string($id), sqlite_escape_string($namespace));
189     if (!$doNotTestCacheValidity)
190     {
191       $statement .= sprintf(" AND created_at > '%s'", sqlite_escape_string($this->refreshTime));
192     }
193
194     $rs = sqlite_query($statement, $this->conn);
195
196     return sqlite_num_rows($rs) ? sqlite_fetch_single($rs) : null;
197   }
198
199   /**
200    * Returns true if there is a cache for the given id and namespace.
201    *
202    * @param  string  The cache id
203    * @param  string  The name of the cache namespace
204    * @param  boolean If set to true, the cache validity won't be tested
205    *
206    * @return boolean true if the cache exists, false otherwise
207    *
208    * @see sfCache
209    */
210   public function has($id, $namespace = self::DEFAULT_NAMESPACE, $doNotTestCacheValidity = false)
211   {
212     $statement = sprintf("SELECT id FROM cache WHERE id = '%s' AND namespace = '%s'", sqlite_escape_string($id), sqlite_escape_string($namespace));
213     if (!$doNotTestCacheValidity)
214     {
215       $statement .= sprintf(" AND created_at > '%s'", sqlite_escape_string($this->refreshTime));
216     }
217
218     return sqlite_num_rows(sqlite_query($statement, $this->conn)) ? true : false;
219   }
220  
221  /**
222   * Saves some data in the cache.
223   *
224   * @param string The cache id
225   * @param string The name of the cache namespace
226   * @param string The data to put in cache
227   *
228   * @return boolean true if no problem
229   *
230   * @see sfCache
231   */
232   public function set($id, $namespace = self::DEFAULT_NAMESPACE, $data)
233   {
234     if ($this->automaticCleaningFactor > 0)
235     {
236       $rand = rand(1, $this->automaticCleaningFactor);
237       if ($rand == 1)
238       {
239         $this->clean(false, 'old');
240       }
241     }
242
243     if (!$this->has($id, $namespace, true))
244     {
245       $statement = sprintf("INSERT INTO cache (id, namespace, data, created_at) VALUES ('%s', '%s', '%s', %d)", sqlite_escape_string($id), sqlite_escape_string($namespace), sqlite_escape_string($data), time());
246     }
247     else
248     {
249       $statement = sprintf("UPDATE cache SET data = '%s', created_at = %s WHERE id = '%s' AND namespace = '%s'", sqlite_escape_string($data), time(), sqlite_escape_string($id), sqlite_escape_string($namespace));
250     }
251
252     if (sqlite_query($statement, $this->conn))
253     {
254       return true;
255     }
256
257     return false;
258   }
259
260  /**
261   * Removes an element from the cache.
262   *
263   * @param string The cache id
264   * @param string The name of the cache namespace
265   *
266   * @return boolean true if no problem
267   *
268   * @see sfCache
269   */
270   public function remove($id, $namespace = self::DEFAULT_NAMESPACE)
271   {
272     $statement = sprintf("DELETE FROM cache WHERE id = '%s' AND namespace = '%s'", sqlite_escape_string($id), sqlite_escape_string($namespace));
273     if (sqlite_query($statement, $this->conn))
274     {
275       return true;
276     }
277
278     return false;
279   }
280
281  /**
282   * Cleans the cache.
283   *
284   * If no namespace is specified all cache files will be destroyed
285   * else only cache files of the specified namespace will be destroyed.
286   *
287   * @param string The name of the cache namespace
288   *
289   * @return boolean true if no problem
290   */
291   public function clean($namespace = null, $mode = 'all')
292   {
293     if (!$namespace)
294     {
295       $statement = "DELETE FROM cache";
296     }
297     else
298     {
299       $statement = sprintf("DELETE FROM cache WHERE namespace LIKE '%s%%'", $namespace);
300     }
301
302     if ('old' == $mode)
303     {
304       $statement .= sprintf(" %s created_at < '%s'", $namespace ? 'AND' : 'WHERE', sqlite_escape_string($this->refreshTime));
305     }
306
307     return sqlite_num_rows(sqlite_query($statement, $this->conn)) ? true : false;
308   }
309
310   public function lastModified($id, $namespace = self::DEFAULT_NAMESPACE)
311   {
312     $statement = sprintf("SELECT created_at FROM cache WHERE id = '%s' AND namespace = '%s'", sqlite_escape_string($id), sqlite_escape_string($namespace));
313     $rs = sqlite_query($statement, $this->conn);
314
315     return sqlite_num_rows($rs) ? intval(sqlite_fetch_single($rs)) : 0;
316   }
317 }
318
Note: See TracBrowser for help on using the browser.