Implements: Kohana_Cache_GarbageCollect
Kohana Cache File driver. Provides a file based driver for the Kohana Cache library. This is one of the slowest caching methods.
Below is an example of a file server configuration.
return
array
(
'file'
=>
array
(
// File driver group
'driver'
=>
'file'
,
// using File driver
'cache_dir'
=> APPPATH.
'cache/.kohana_cache'
,
// Cache location
),
)
In cases where only one cache group is required, if the group is named default
there is
no need to pass the group name when instantiating a cache instance.
Below are the settings available to all types of cache driver.
Name | Required | Description |
---|---|---|
driver | YES | (string) The driver type to use |
cache_dir | NO | (string) The cache directory to use for this cache instance |
Class declared in MODPATH/cache/classes/kohana/cache/file.php on line 41.
integer 3600
string
$defaultlink to thisdefault driver to use
string(4) "file"
Kohana_Cache
$instanceslink to thisinstances
array(0)
string
$_cache_dirlink to thisthe caching directory
Config
$_configlink to thisDelete a cache entry based on id
// Delete 'foo' entry from the file group
Cache::instance(
'file'
)->
delete
(
'foo'
);
string
$id
required - Id to remove from cacheboolean
public
function
delete
(
$id
)
{
$filename
= Cache_File::filename(
$this
->_sanitize_id(
$id
));
$directory
=
$this
->_resolve_directory(
$filename
);
return
$this
->_delete_file(
new
SplFileInfo(
$directory
.
$filename
), NULL, TRUE);
}
Delete all cache entries.
Beware of using this method when using shared memory cache systems, as it will wipe every entry within the system for all clients.
// Delete all cache entries in the file group
Cache::instance(
'file'
)->delete_all();
boolean
public
function
delete_all()
{
return
$this
->_delete_file(
$this
->_cache_dir, TRUE);
}
Garbage collection method that cleans any expired cache entries from the cache.
void
public
function
garbage_collect()
{
$this
->_delete_file(
$this
->_cache_dir, TRUE, FALSE, TRUE);
return
;
}
Retrieve a cached value entry by id.
// Retrieve cache entry from file group
$data
= Cache::instance(
'file'
)->get(
'foo'
);
// Retrieve cache entry from file group and return 'bar' if miss
$data
= Cache::instance(
'file'
)->get(
'foo'
,
'bar'
);
string
$id
required - Id of cache to entrystring
$default
= NULL - Default value to return if cache missmixed
public
function
get(
$id
,
$default
= NULL)
{
$filename
= Cache_File::filename(
$this
->_sanitize_id(
$id
));
$directory
=
$this
->_resolve_directory(
$filename
);
// Wrap operations in try/catch to handle notices
try
{
// Open file
$file
=
new
SplFileInfo(
$directory
.
$filename
);
// If file does not exist
if
( !
$file
->isFile())
{
// Return default value
return
$default
;
}
else
{
// Open the file and extract the json
$json
=
$file
->openFile()->current();
// Decode the json into PHP object
$data
= json_decode(
$json
);
// Test the expiry
if
(
$data
->expiry < time())
{
// Delete the file
$this
->_delete_file(
$file
, NULL, TRUE);
// Return default value
return
$default
;
}
else
{
return
(
$data
->type ===
'string'
) ?
$data
->payload : unserialize(
$data
->payload);
}
}
}
catch
(ErrorException
$e
)
{
// Handle ErrorException caused by failed unserialization
if
(
$e
->getCode() === E_NOTICE)
{
throw
new
Kohana_Cache_Exception(
__METHOD__
.
' failed to unserialize cached object with message : '
.
$e
->getMessage());
}
// Otherwise throw the exception
throw
$e
;
}
}
Set a value to cache with id and lifetime
$data
=
'bar'
;
// Set 'bar' to 'foo' in file group, using default expiry
Cache::instance(
'file'
)->set(
'foo'
,
$data
);
// Set 'bar' to 'foo' in file group for 30 seconds
Cache::instance(
'file'
)->set(
'foo'
,
$data
, 30);
string
$id
required - Id of cache entrystring
$data
required - Data to set to cacheinteger
$lifetime
= NULL - Lifetime in secondsboolean
public
function
set(
$id
,
$data
,
$lifetime
= NULL)
{
$filename
= Cache_File::filename(
$this
->_sanitize_id(
$id
));
$directory
=
$this
->_resolve_directory(
$filename
);
// If lifetime is NULL
if
(
$lifetime
=== NULL)
{
// Set to the default expiry
$lifetime
= Arr::get(
$this
->_config,
'default_expire'
, Cache::DEFAULT_EXPIRE);
}
// Open directory
$dir
=
new
SplFileInfo(
$directory
);
// If the directory path is not a directory
if
( !
$dir
->isDir())
{
// Create the directory
if
( !
mkdir
(
$directory
, 0777, TRUE))
{
throw
new
Kohana_Cache_Exception(
__METHOD__
.
' unable to create directory : :directory'
,
array
(
':directory'
=>
$directory
));
}
// chmod to solve potential umask issues
chmod
(
$directory
, 0777);
}
// Open file to inspect
$resouce
=
new
SplFileInfo(
$directory
.
$filename
);
$file
=
$resouce
->openFile(
'w'
);
try
{
$type
=
gettype
(
$data
);
// Serialize the data
$data
= json_encode( (object)
array
(
'payload'
=> (
$type
===
'string'
) ?
$data
: serialize(
$data
),
'expiry'
=> time() +
$lifetime
,
'type'
=>
$type
));
$size
=
strlen
(
$data
);
}
catch
(ErrorException
$e
)
{
// If serialize through an error exception
if
(
$e
->getCode() === E_NOTICE)
{
// Throw a caching error
throw
new
Kohana_Cache_Exception(
__METHOD__
.
' failed to serialize data for caching with message : '
.
$e
->getMessage());
}
// Else rethrow the error exception
throw
$e
;
}
try
{
$file
->fwrite(
$data
,
$size
);
return
(bool)
$file
->
fflush
();
}
catch
(Exception
$e
)
{
throw
$e
;
}
}
Overload the __clone() method to prevent cloning
void
public
function
__clone()
{
throw
new
Kohana_Cache_Exception(
'Cloning of Kohana_Cache objects is forbidden'
);
}
Creates a singleton of a Kohana Cache group. If no group is supplied the default cache group is used.
// Create an instance of the default group
$default_group
= Cache::instance();
// Create an instance of a group
$foo_group
= Cache::instance(
'foo'
);
// Access an instantiated group directly
$foo_group
= Cache::
$instances
[
'default'
];
string
$group
= NULL - The name of the cache group to use [Optional]Cache
public
static
function
instance(
$group
= NULL)
{
// If there is no group supplied
if
(
$group
=== NULL)
{
// Use the default setting
$group
= Cache::
$default
;
}
if
(isset(Cache::
$instances
[
$group
]))
{
// Return the current group if initiated already
return
Cache::
$instances
[
$group
];
}
$config
= Kohana::config(
'cache'
);
if
( !
$config
->offsetExists(
$group
))
{
throw
new
Kohana_Cache_Exception(
'Failed to load Kohana Cache group: :group'
,
array
(
':group'
=>
$group
));
}
$config
=
$config
->get(
$group
);
// Create a new cache type instance
$cache_class
=
'Cache_'
.ucfirst(
$config
[
'driver'
]);
Cache::
$instances
[
$group
] =
new
$cache_class
(
$config
);
// Return the instance
return
Cache::
$instances
[
$group
];
}
Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must
be instantiated using the Cache::instance()
method.
array
$config
required - Config
protected
function
__construct(
array
$config
)
{
// Setup parent
parent::__construct(
$config
);
try
{
$directory
= Arr::get(
$this
->_config,
'cache_dir'
, Kohana::
$cache_dir
);
$this
->_cache_dir =
new
SplFileInfo(
$directory
);
}
// PHP < 5.3 exception handle
catch
(ErrorException
$e
)
{
$this
->_cache_dir =
$this
->_make_directory(
$directory
, 0777, TRUE);
}
// PHP >= 5.3 exception handle
catch
(UnexpectedValueException
$e
)
{
$this
->_cache_dir =
$this
->_make_directory(
$directory
, 0777, TRUE);
}
// If the defined directory is a file, get outta here
if
(
$this
->_cache_dir->isFile())
{
throw
new
Kohana_Cache_Exception(
'Unable to create cache directory as a file already exists : :resource'
,
array
(
':resource'
=>
$this
->_cache_dir->getRealPath()));
}
// Check the read status of the directory
if
( !
$this
->_cache_dir->isReadable())
{
throw
new
Kohana_Cache_Exception(
'Unable to read from the cache directory :resource'
,
array
(
':resource'
=>
$this
->_cache_dir->getRealPath()));
}
// Check the write status of the directory
if
( !
$this
->_cache_dir->isWritable())
{
throw
new
Kohana_Cache_Exception(
'Unable to write to the cache directory :resource'
,
array
(
':resource'
=>
$this
->_cache_dir->getRealPath()));
}
}
Deletes files recursively and returns FALSE on any errors
// Delete a file or folder whilst retaining parent directory and ignore all errors
$this
->_delete_file(
$folder
, TRUE, TRUE);
SplFileInfo
$file
required - Fileboolean
$retain_parent_directory
= bool FALSE - Retain the parent directoryboolean
$ignore_errors
= bool FALSE - Ignore_errors to prevent all exceptions interrupting execboolean
$only_expired
= bool FALSE - Only expired filesboolean
protected
function
_delete_file(SplFileInfo
$file
,
$retain_parent_directory
= FALSE,
$ignore_errors
= FALSE,
$only_expired
= FALSE)
{
// Allow graceful error handling
try
{
// If is file
if
(
$file
->isFile())
{
try
{
// If only expired is not set
if
(
$only_expired
=== FALSE)
{
// We want to delete the file
$delete
= TRUE;
}
// Otherwise...
else
{
// Assess the file expiry to flag it for deletion
$json
=
$file
->openFile(
'r'
)->current();
$data
= json_decode(
$json
);
$delete
=
$data
->expiry < time();
}
// If the delete flag is set
if
(
$delete
=== TRUE)
{
// Try to delete
unlink(
$file
->getRealPath());
}
}
catch
(ErrorException
$e
)
{
// Catch any delete file warnings
if
(
$e
->getCode() === E_WARNING)
{
throw
new
Kohana_Cache_Exception(
__METHOD__
.
' failed to delete file : :file'
,
array
(
':file'
=>
$file
->getRealPath()));
}
}
}
// Else, is directory
elseif
(
$file
->isDir())
{
// Create new DirectoryIterator
$files
=
new
DirectoryIterator(
$file
->getPathname());
// Iterate over each entry
while
(
$files
->valid())
{
// Extract the entry name
$name
=
$files
->getFilename();
// If the name is not a dot
if
(
$name
!=
'.'
AND
$name
!=
'..'
AND
substr
(
$file
->getFilename(), 0, 1) ==
'.'
)
{
// Create new file resource
$fp
=
new
SplFileInfo(
$files
->getRealPath());
// Delete the file
$this
->_delete_file(
$fp
);
}
// Move the file pointer on
$files
->next();
}
// If set to retain parent directory, return now
if
(
$retain_parent_directory
)
{
return
TRUE;
}
try
{
// Remove the files iterator
// (fixes Windows PHP which has permission issues with open iterators)
unset(
$files
);
// Try to remove the parent directory
return
rmdir
(
$file
->getRealPath());
}
catch
(ErrorException
$e
)
{
// Catch any delete directory warnings
if
(
$e
->getCode() === E_WARNING)
{
throw
new
Kohana_Cache_Exception(
__METHOD__
.
' failed to delete directory : :directory'
,
array
(
':directory'
=>
$file
->getRealPath()));
}
}
}
}
// Catch all exceptions
catch
(Exception
$e
)
{
// If ignore_errors is on
if
(
$ignore_errors
=== TRUE)
{
// Return
return
FALSE;
}
// Throw exception
throw
$e
;
}
}
Makes the cache directory if it doesn't exist. Simply a wrapper for
mkdir
to ensure DRY principles
string
$directory
required - $directoryinteger
$mode
= integer 511 - $modeboolean
$recursive
= bool FALSE - $recursiveresource
$context
= NULL - $contextSplFileInfo
protected
function
_make_directory(
$directory
,
$mode
= 0777,
$recursive
= FALSE,
$context
= NULL)
{
if
( !
mkdir
(
$directory
,
$mode
,
$recursive
,
$context
))
{
throw
new
Kohana_Cache_Exception(
'Failed to create the defined cache directory : :directory'
,
array
(
':directory'
=>
$directory
));
}
chmod
(
$directory
,
$mode
);
return
new
SplFileInfo(
$directory
);
}
Resolves the cache directory real path from the filename
// Get the realpath of the cache folder
$realpath
=
$this
->_resolve_directory(
$filename
);
string
$filename
required - Filename to resolvestring
protected
function
_resolve_directory(
$filename
)
{
return
$this
->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.
$filename
[0].
$filename
[1].DIRECTORY_SEPARATOR;
}
Creates a hashed filename based on the string. This is used to create shorter unique IDs for each cache filename.
// Create the cache filename
$filename
= Cache_File::filename(
$this
->_sanitize_id(
$id
));
string
$string
required - String to hash into filenamestring
protected
static
function
filename(
$string
)
{
return
sha1(
$string
).
'.json'
;
}
Replaces troublesome characters with underscores.
// Sanitize a cache id
$id
=
$this
->_sanitize_id(
$id
);
string
$id
required - Id of cache to sanitizestring
protected
function
_sanitize_id(
$id
)
{
// Change slashes and spaces to underscores
return
str_replace
(
array
(
'/'
,
'\\'
,
' '
),
'_'
,
$id
);
}