summaryrefslogtreecommitdiff
path: root/kohana/libraries/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'kohana/libraries/drivers')
-rw-r--r--kohana/libraries/drivers/Cache.php2
-rw-r--r--kohana/libraries/drivers/Cache/Apc.php27
-rw-r--r--kohana/libraries/drivers/Cache/Eaccelerator.php29
-rw-r--r--kohana/libraries/drivers/Cache/File.php94
-rw-r--r--kohana/libraries/drivers/Cache/Memcache.php142
-rw-r--r--kohana/libraries/drivers/Cache/Sqlite.php101
-rw-r--r--kohana/libraries/drivers/Cache/Xcache.php7
7 files changed, 298 insertions, 104 deletions
diff --git a/kohana/libraries/drivers/Cache.php b/kohana/libraries/drivers/Cache.php
index 0e825bcf..6f24ccac 100644
--- a/kohana/libraries/drivers/Cache.php
+++ b/kohana/libraries/drivers/Cache.php
@@ -14,7 +14,7 @@ interface Cache_Driver {
/**
* Set a cache item.
*/
- public function set($id, $data, $tags, $lifetime);
+ public function set($id, $data, array $tags = NULL, $lifetime);
/**
* Find all of the cache ids for a given tag.
diff --git a/kohana/libraries/drivers/Cache/Apc.php b/kohana/libraries/drivers/Cache/Apc.php
index 39d47dd3..29431ed4 100644
--- a/kohana/libraries/drivers/Cache/Apc.php
+++ b/kohana/libraries/drivers/Cache/Apc.php
@@ -22,27 +22,38 @@ class Cache_Apc_Driver implements Cache_Driver {
return (($return = apc_fetch($id)) === FALSE) ? NULL : $return;
}
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
- count($tags) and Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
+ if ( ! empty($tags))
+ {
+ Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
+ }
return apc_store($id, $data, $lifetime);
}
public function find($tag)
{
- return FALSE;
+ Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
+
+ return array();
}
public function delete($id, $tag = FALSE)
{
- if ($id === TRUE)
+ if ($tag === TRUE)
+ {
+ Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
+ return FALSE;
+ }
+ elseif ($id === TRUE)
+ {
return apc_clear_cache('user');
-
- if ($tag == FALSE)
+ }
+ else
+ {
return apc_delete($id);
-
- return TRUE;
+ }
}
public function delete_expired()
diff --git a/kohana/libraries/drivers/Cache/Eaccelerator.php b/kohana/libraries/drivers/Cache/Eaccelerator.php
index 8fd32471..0710d483 100644
--- a/kohana/libraries/drivers/Cache/Eaccelerator.php
+++ b/kohana/libraries/drivers/Cache/Eaccelerator.php
@@ -24,30 +24,43 @@ class Cache_Eaccelerator_Driver implements Cache_Driver {
public function find($tag)
{
- return FALSE;
+ Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
+
+ return array();
}
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
- count($tags) and Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
+ if ( ! empty($tags))
+ {
+ Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
+ }
return eaccelerator_put($id, $data, $lifetime);
}
public function delete($id, $tag = FALSE)
{
- if ($id === TRUE)
+ if ($tag === TRUE)
+ {
+ Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
+ return FALSE;
+ }
+ elseif ($id === TRUE)
+ {
return eaccelerator_clean();
-
- if ($tag == FALSE)
+ }
+ else
+ {
return eaccelerator_rm($id);
-
- return TRUE;
+ }
}
public function delete_expired()
{
eaccelerator_gc();
+
+ return TRUE;
}
} // End Cache eAccelerator Driver \ No newline at end of file
diff --git a/kohana/libraries/drivers/Cache/File.php b/kohana/libraries/drivers/Cache/File.php
index 16fa08e1..db27f02d 100644
--- a/kohana/libraries/drivers/Cache/File.php
+++ b/kohana/libraries/drivers/Cache/File.php
@@ -35,25 +35,25 @@ class Cache_File_Driver implements Cache_Driver {
* @param string cache id or tag
* @param bool search for tags
* @return array of filenames matching the id or tag
- * @return void if no matching files are found
*/
public function exists($id, $tag = FALSE)
{
if ($id === TRUE)
{
// Find all the files
- $files = glob($this->directory.'*~*~*');
+ return glob($this->directory.'*~*~*');
}
- elseif ($tag == TRUE)
+ elseif ($tag === TRUE)
{
// Find all the files that have the tag name
- $files = glob($this->directory.'*~*'.$id.'*~*');
+ $paths = glob($this->directory.'*~*'.$id.'*~*');
// Find all tags matching the given tag
- foreach ($files as $i => $file)
+ $files = array();
+ foreach ($paths as $path)
{
// Split the files
- $tags = explode('~', $file);
+ $tags = explode('~', basename($path));
// Find valid tags
if (count($tags) !== 3 OR empty($tags[1]))
@@ -62,20 +62,20 @@ class Cache_File_Driver implements Cache_Driver {
// Split the tags by plus signs, used to separate tags
$tags = explode('+', $tags[1]);
- if ( ! in_array($tag, $tags))
+ if (in_array($tag, $tags))
{
- // This entry does not match the tag
- unset($files[$i]);
+ // Add the file to the array, it has the requested tag
+ $files[] = $path;
}
}
+
+ return $files;
}
else
{
- // Find all the files matching the given id
- $files = glob($this->directory.$id.'~*');
+ // Find the file matching the given id
+ return glob($this->directory.$id.'~*');
}
-
- return empty($files) ? NULL : $files;
}
/**
@@ -87,7 +87,7 @@ class Cache_File_Driver implements Cache_Driver {
* @param integer lifetime
* @return bool
*/
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
// Remove old cache files
$this->delete($id);
@@ -98,11 +98,14 @@ class Cache_File_Driver implements Cache_Driver {
$lifetime += time();
}
- // Construct the filename
- $filename = $id.'~'.implode('+', $tags).'~'.$lifetime;
+ if ( ! empty($tags))
+ {
+ // Convert the tags into a string list
+ $tags = implode('+', $tags);
+ }
- // Write the file, appending the sha1 signature to the beginning of the data
- return (bool) file_put_contents($this->directory.$filename, sha1($data).$data);
+ // Write out a serialized cache
+ return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, serialize($data));
}
/**
@@ -113,23 +116,29 @@ class Cache_File_Driver implements Cache_Driver {
*/
public function find($tag)
{
- if ($files = $this->exists($tag, TRUE))
+ // An array will always be returned
+ $result = array();
+
+ if ($paths = $this->exists($tag, TRUE))
{
// Length of directory name
$offset = strlen($this->directory);
// Find all the files with the given tag
- $array = array();
- foreach ($files as $file)
+ foreach ($paths as $path)
{
// Get the id from the filename
- $array[] = substr(current(explode('~', $file)), $offset);
- }
+ list($id, $junk) = explode('~', basename($path), 2);
- return $array;
+ if (($data = $this->get($id)) !== FALSE)
+ {
+ // Add the result to the array
+ $result[$id] = $data;
+ }
+ }
}
- return FALSE;
+ return $result;
}
/**
@@ -143,7 +152,7 @@ class Cache_File_Driver implements Cache_Driver {
{
if ($file = $this->exists($id))
{
- // Always process the first result
+ // Use the first file
$file = current($file);
// Validate that the cache has not expired
@@ -154,22 +163,22 @@ class Cache_File_Driver implements Cache_Driver {
}
else
{
- $data = file_get_contents($file);
-
- // Find the hash of the data
- $hash = substr($data, 0, 40);
-
- // Remove the hash from the data
- $data = substr($data, 40);
+ // Turn off errors while reading the file
+ $ER = error_reporting(0);
- if ($hash !== sha1($data))
+ if (($data = file_get_contents($file)) !== FALSE)
{
- // Remove this cache, it doesn't validate
- $this->delete($id);
-
- // Unset data to prevent it from being returned
+ // Unserialize the data
+ $data = unserialize($data);
+ }
+ else
+ {
+ // Delete the data
unset($data);
}
+
+ // Turn errors back on
+ error_reporting($ER);
}
}
@@ -216,14 +225,21 @@ class Cache_File_Driver implements Cache_Driver {
{
if ($files = $this->exists(TRUE))
{
+ // Disable all error reporting while deleting
+ $ER = error_reporting(0);
+
foreach ($files as $file)
{
if ($this->expired($file))
{
// The cache file has already expired, delete it
- @unlink($file) or Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
+ if ( ! unlink($file))
+ Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
}
}
+
+ // Turn on error reporting again
+ error_reporting($ER);
}
}
diff --git a/kohana/libraries/drivers/Cache/Memcache.php b/kohana/libraries/drivers/Cache/Memcache.php
index ef5b8440..f443b997 100644
--- a/kohana/libraries/drivers/Cache/Memcache.php
+++ b/kohana/libraries/drivers/Cache/Memcache.php
@@ -11,17 +11,28 @@
*/
class Cache_Memcache_Driver implements Cache_Driver {
+ const TAGS_KEY = 'memcache_tags_array';
+
// Cache backend object and flags
protected $backend;
protected $flags;
+ // The persistent lifetime value for expirations of 0
+ protected $persistent_lifetime;
+
+ // Tags array
+ protected $tags;
+
+ // Have the tags been changed?
+ protected $tags_changed = FALSE;
+
public function __construct()
{
if ( ! extension_loaded('memcache'))
throw new Kohana_Exception('cache.extension_not_loaded', 'memcache');
$this->backend = new Memcache;
- $this->flags = Kohana::config('cache_memcache.compression') ? MEMCACHE_COMPRESSED : 0;
+ $this->flags = Kohana::config('cache_memcache.compression') ? MEMCACHE_COMPRESSED : FALSE;
$servers = Kohana::config('cache_memcache.servers');
@@ -34,11 +45,44 @@ class Cache_Memcache_Driver implements Cache_Driver {
$this->backend->addServer($server['host'], $server['port'], (bool) $server['persistent'])
or Kohana::log('error', 'Cache: Connection failed: '.$server['host']);
}
+
+ // Set "persistent lifetime" value to one year
+ $this->persistent_lifetime = strtotime('now +1 year');
+
+ // Load tags
+ $this->tags = $this->backend->get(self::TAGS_KEY);
+
+ if ( ! is_array($this->tags))
+ {
+ // Create a new tags array
+ $this->tags = array();
+
+ // Tags have been created
+ $this->tags_changed = TRUE;
+ }
+ }
+
+ public function __destruct()
+ {
+ if ($this->tags_changed === TRUE)
+ {
+ // Save the tags
+ $this->backend->set(self::TAGS_KEY, $this->tags, $this->flags, $this->persistent_lifetime);
+ }
}
public function find($tag)
{
- return FALSE;
+ if (isset($this->tags[$tag]) AND $results = $this->backend->get($this->tags[$tag]))
+ {
+ // Return all the found caches
+ return $results;
+ }
+ else
+ {
+ // No matching tags
+ return array();
+ }
}
public function get($id)
@@ -46,32 +90,110 @@ class Cache_Memcache_Driver implements Cache_Driver {
return (($return = $this->backend->get($id)) === FALSE) ? NULL : $return;
}
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
- count($tags) and Kohana::log('error', 'Cache: Tags are unsupported by the memcache driver');
+ if ( ! empty($tags))
+ {
+ // Tags will be changed
+ $this->tags_changed = TRUE;
+
+ foreach ($tags as $tag)
+ {
+ // Add the id to each tag
+ $this->tags[$tag][$id] = $id;
+ }
+ }
- // Memcache driver expects unix timestamp
- if ($lifetime !== 0)
+ if ($lifetime === 0)
{
+ // Using an expiration of zero is unreliable, as memcache may delete
+ // it without warning. @see http://php.net/memcache_set
+ $lifetime = $this->persistent_lifetime;
+ }
+ else
+ {
+ // Memcache driver expects unix timestamp
$lifetime += time();
}
+ // Set a new value
return $this->backend->set($id, $data, $this->flags, $lifetime);
}
public function delete($id, $tag = FALSE)
{
+ // Tags will be changed
+ $this->tags_changed = TRUE;
+
if ($id === TRUE)
- return $this->backend->flush();
+ {
+ if ($status = $this->backend->flush())
+ {
+ // Remove all tags, all items have been deleted
+ $this->tags = array();
- if ($tag == FALSE)
- return $this->backend->delete($id);
+ // We must sleep after flushing, or overwriting will not work!
+ // @see http://php.net/manual/en/function.memcache-flush.php#81420
+ sleep(1);
+ }
- return TRUE;
+ return $status;
+ }
+ elseif ($tag === TRUE)
+ {
+ if (isset($this->tags[$id]))
+ {
+ foreach ($this->tags[$id] as $_id)
+ {
+ // Delete each id in the tag
+ $this->backend->delete($_id);
+ }
+
+ // Delete the tag
+ unset($this->tags[$id]);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ foreach ($this->tags as $tag => $_ids)
+ {
+ if (isset($this->tags[$tag][$id]))
+ {
+ // Remove the id from the tags
+ unset($this->tags[$tag][$id]);
+ }
+ }
+
+ return $this->backend->delete($id);
+ }
}
public function delete_expired()
{
+ // Tags will be changed
+ $this->tags_changed = TRUE;
+
+ foreach ($this->tags as $tag => $_ids)
+ {
+ foreach ($_ids as $id)
+ {
+ if ( ! $this->backend->get($id))
+ {
+ // This id has disappeared, delete it from the tags
+ unset($this->tags[$tag][$id]);
+ }
+ }
+
+ if (empty($this->tags[$tag]))
+ {
+ // The tag no longer has any valid ids
+ unset($this->tags[$tag]);
+ }
+ }
+
+ // Memcache handles garbage collection internally
return TRUE;
}
diff --git a/kohana/libraries/drivers/Cache/Sqlite.php b/kohana/libraries/drivers/Cache/Sqlite.php
index 7413b08c..a86efaba 100644
--- a/kohana/libraries/drivers/Cache/Sqlite.php
+++ b/kohana/libraries/drivers/Cache/Sqlite.php
@@ -91,16 +91,16 @@ class Cache_Sqlite_Driver implements Cache_Driver {
* @param integer lifetime
* @return bool
*/
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
- // Find the data hash
- $hash = sha1($data);
+ // Serialize and escape the data
+ $data = sqlite_escape_string(serialize($data));
- // Escape the data
- $data = sqlite_escape_string($data);
-
- // Escape the tags
- $tags = sqlite_escape_string(implode(',', $tags));
+ if ( ! empty($tags))
+ {
+ // Escape the tags, adding brackets so the tag can be explicitly matched
+ $tags = sqlite_escape_string('<'.implode('>,<', $tags).'>');
+ }
// Cache Sqlite driver expects unix timestamp
if ($lifetime !== 0)
@@ -109,15 +109,21 @@ class Cache_Sqlite_Driver implements Cache_Driver {
}
$query = $this->exists($id)
- ? "UPDATE caches SET hash = '$hash', tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'"
- : "INSERT INTO caches VALUES('$id', '$hash', '$tags', '$lifetime', '$data')";
+ ? "UPDATE caches SET tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'"
+ : "INSERT INTO caches VALUES('$id', '$tags', '$lifetime', '$data')";
// Run the query
$this->db->unbufferedQuery($query, SQLITE_BOTH, $error);
- empty($error) or self::log_error($error);
-
- return empty($error);
+ if ( ! empty($error))
+ {
+ self::log_error($error);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
}
/**
@@ -128,23 +134,32 @@ class Cache_Sqlite_Driver implements Cache_Driver {
*/
public function find($tag)
{
- $query = "SELECT id FROM caches WHERE tags LIKE '%{$tag}%'";
+ $query = "SELECT id,cache FROM caches WHERE tags LIKE '%<{$tag}>%'";
$query = $this->db->query($query, SQLITE_BOTH, $error);
- empty($error) or self::log_error($error);
+ // An array will always be returned
+ $result = array();
- if (empty($error) AND $query->numRows() > 0)
+ if ( ! empty($error))
+ {
+ self::log_error($error);
+ }
+ elseif ($query->numRows() > 0)
{
- $array = array();
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+
while ($row = $query->fetchObject())
{
- // Add each id to the array
- $array[] = $row->id;
+ // Add each cache to the array
+ $result[$row->id] = unserialize($row->cache);
}
- return $array;
+
+ // Turn notices back on
+ error_reporting($ER);
}
- return FALSE;
+ return $result;
}
/**
@@ -156,23 +171,31 @@ class Cache_Sqlite_Driver implements Cache_Driver {
*/
public function get($id)
{
- $query = "SELECT id, hash, expiration, cache FROM caches WHERE id = '{$id}' LIMIT 0, 1";
+ $query = "SELECT id, expiration, cache FROM caches WHERE id = '$id' LIMIT 0, 1";
$query = $this->db->query($query, SQLITE_BOTH, $error);
- empty($error) or self::log_error($error);
-
- if (empty($error) AND $cache = $query->fetchObject())
+ if ( ! empty($error))
+ {
+ self::log_error($error);
+ }
+ elseif ($cache = $query->fetchObject())
{
// Make sure the expiration is valid and that the hash matches
- if (($cache->expiration != 0 AND $cache->expiration <= time()) OR $cache->hash !== sha1($cache->cache))
+ if ($cache->expiration != 0 AND $cache->expiration <= time())
{
// Cache is not valid, delete it now
$this->delete($cache->id);
}
else
{
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+
// Return the valid cache data
- return $cache->cache;
+ $data = $cache->cache;
+
+ // Turn notices back on
+ error_reporting($ER);
}
}
@@ -184,7 +207,7 @@ class Cache_Sqlite_Driver implements Cache_Driver {
* Deletes a cache item by id or tag
*
* @param string cache id or tag, or TRUE for "all items"
- * @param bool use tags
+ * @param bool delete a tag
* @return bool
*/
public function delete($id, $tag = FALSE)
@@ -194,22 +217,28 @@ class Cache_Sqlite_Driver implements Cache_Driver {
// Delete all caches
$where = '1';
}
- elseif ($tag == FALSE)
+ elseif ($tag === TRUE)
{
- // Delete by id
- $where = "id = '{$id}'";
+ // Delete by tag
+ $where = "tags LIKE '%<{$id}>%'";
}
else
{
- // Delete by tag
- $where = "tags LIKE '%{$tag}%'";
+ // Delete by id
+ $where = "id = '$id'";
}
$this->db->unbufferedQuery('DELETE FROM caches WHERE '.$where, SQLITE_BOTH, $error);
- empty($error) or self::log_error($error);
-
- return empty($error);
+ if ( ! empty($error))
+ {
+ self::log_error($error);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
}
/**
diff --git a/kohana/libraries/drivers/Cache/Xcache.php b/kohana/libraries/drivers/Cache/Xcache.php
index 5d027eec..8d31993a 100644
--- a/kohana/libraries/drivers/Cache/Xcache.php
+++ b/kohana/libraries/drivers/Cache/Xcache.php
@@ -25,9 +25,12 @@ class Cache_Xcache_Driver implements Cache_Driver {
return NULL;
}
- public function set($id, $data, $tags, $lifetime)
+ public function set($id, $data, array $tags = NULL, $lifetime)
{
- count($tags) and Kohana::log('error', 'Cache: tags are unsupported by the Xcache driver');
+ if ( ! empty($tags))
+ {
+ Kohana::log('error', 'Cache: tags are unsupported by the Xcache driver');
+ }
return xcache_set($id, $data, $lifetime);
}