<?php
/**
* Filename.......: cachedConfig.php
* Project........: V-webmail
* Last Modified..: $Date: 2006/01/28 15:19:23 $
* CVS Revision...: $Revision: 1.3 $
* Copyright......: 2001-2004 Richard Heyes
*/
require_once($CONFIG['pear_dir'] . 'XML/Tree.php');
/**
* Reads an XML configuration file and caches it
* as a PHP includable file.
*/
class cachedConfig
{
/**
* Filename of config file
* @var string
*/
var $filename;
/**
* Path to cache dir
* @var string
*/
var $cacheDir;
/**
* The configuration data
* @var array
*/
var $config;
/**
* Base path for accessing configuration data
* @var string
*/
var $basePath;
/**
* List of errors if they occur
* @var array
*/
var $errors;
/**
* Constructor. Pass it the filename of the configuration file
* and the directory path to use for caching (null for no caching).
* The filename will be cached in the cache directory with ".tmp"
* appended to the filename.
*
* @param string $filename Filename of config file
* @param string $cacheDir Path to cache dir. If this
* is null then no caching will
* occur. Must have a trailing slash.
*/
function cachedConfig($filename, $cacheDir = null)
{
$this->filename = $filename;
$this->cacheDir = $cacheDir;
// Check if the file is cached, if so include it
// and set the data appropriately.
if (!is_null($cacheDir)
&& $this->isCached()) {
$cacheFilemtime = filemtime($this->cacheDir . basename($this->filename) . '.tmp');
$configFilemtime = filemtime($this->filename);
if ($configFilemtime < $cacheFilemtime) {
include($this->cacheDir . basename($this->filename) . '.tmp');
$this->config = $config;
return;
}
}
$config = null;
$result = $this->readConfiguration($config);
if ($result) {
$this->config = $config;
if (!is_null($cacheDir)) {
if (!$this->saveConfiguration()) {
$this->errors[] = 'Failed to save configuration';
}
}
} else {
$this->errors[] = 'Error reading configuration file';
}
}
/**
* Returns true/false as to whether the configuration file
* is cached or not.
*
* @return bool Whether the config file is cached or not
*/
function isCached()
{
return file_exists($this->cacheDir . basename($this->filename) . '.tmp');
}
/**
* Reads the configuration from the already specified
* filename
*
* @param object $output Output from XML tree
* @return true/false indicating status
*/
function readConfiguration(&$output)
{
if (file_exists($this->filename)) {
// Start parsing config file.
$xmlTree = new XML_Tree($this->filename);
$rootNode = &$xmlTree->getTreeFromFile();
if (PEAR::isError($rootNode)) {
return false;
}
// Convert the XML Tree to a structured PHP array
$phpArray[$rootNode->name] = $this->xmlTree2PHPArray($rootNode);
$output = $phpArray;
return true;
}
return false;
}
/**
* Saves the configuration to a file in the cache directory
*
* @return bool Success or not in writing the file out
*/
function saveConfiguration()
{
$string = '$config = ' . $this->toPHPCode($this->config);
$string = "<?php\r\n$string;\r\n?>";
$path = $this->cacheDir . basename($this->filename) . '.tmp';
if (is_writeable($this->cacheDir)) {
$fp = fopen($path, 'wb');
$bytesWritten = fwrite($fp, $string);
fclose($fp);
// Check bytes written matches string length
if ($bytesWritten == strlen($string)) {
return true;
} else {
$this->errors[] = "Bytes written and length of string didn't match";
return false;
}
} else {
$this->errors[] = "Path ($path) not writeable";
}
}
/**
* Converts an XML tree to a PHP array
*
* @param object $tree The XML_Tree object
*/
function xmlTree2PHPArray($tree)
{
$arr = array();
if (!empty($tree->children)) {
foreach ($tree->children as $child) {
// Already multiple entries for this key, append another
if (!empty($arr[$child->name][0])) {
$arr[$child->name][] = $this->xmlTree2PHPArray($child);
// Only one entry for this key, convert to indexed array
} else if (!empty($arr[$child->name])) {
$arr[$child->name] = array($arr[$child->name]);
$arr[$child->name][] = $this->xmlTree2PHPArray($child);
// Key is not currently defined and multiple attribute is specified
} else if (@$child->attributes['multiple']) {
$arr[$child->name] = array($this->xmlTree2PHPArray($child));
// Key is not currently defined and multiple attribute is not specified
} else {
$arr[$child->name] = $this->xmlTree2PHPArray($child);
}
}
} else {
$arr = $tree->content;
if (!empty($tree->attributes['type'])) {
if ($tree->attributes['type'] == 'boolean' AND ($arr == 'true' OR $arr == 'false') ) {
$arr = ($arr == 'true' ? true : false);
} else {
settype($arr, $tree->attributes['type']);
}
}
}
return $arr;
}
/**
* Converts a PHP array to a string as valid PHP code. If
* written to a file, allows that file to be include()ed,
* thus recreating the array.
*
* @param array $input Input array
* @return string Resulting string
*/
function toPHPCode($input)
{
switch (true) {
case is_array($input):
foreach ($input as $key => $value) {
$str[] = $this->toPHPCode($key) . " => " . $this->toPHPCode($value);
}
return 'array(' . implode(', ', $str) . ')';
case is_string($input):
return "'" . strtr($input, array('\\' => '\\\\', "'" => "\\'")) . "'";
case is_bool($input):
return $input ? 'true' : 'false';
case is_int($input):
case is_double($input):
case is_float($input):
return $input;
case is_object($input):
case is_resource($input):
case is_null($input):
return 'null';
break;
}
}
/**
* Sets a base path for getting configuration variables.
*
* @param string $basePath Base path to set
*/
function setBasePath($basePath)
{
$this->basePath = $basePath;
}
/**
* Retrieves a value from the config
*
* @param string $path Path of item to get
* @param string $type Default type to return if value doesn't
* exist.
* @return mixed Value of config item
*/
function getValue($path, $type = 'string')
{
// Prepend any base path
if (!empty($this->basePath) AND $path[0] != '/') {
$path = $this->basePath . $path;
// Bypass base root by putting / at start of path, though
// we need to snip it off here.
} else if ($path[0] == '/') {
$path = substr($path, 1);
}
// Explode path based on /
$path = explode('/', $path);
if (!empty($path)) {
$localConfig = $this->config;
foreach ($path as $key) {
if (isset($localConfig[$key])) {
$localConfig = $localConfig[$key];
} else {
$localConfig = null;
}
}
if (!is_null($localConfig)) {
return $localConfig;
}
}
// Return default type
$retval = '';
settype($retval, $type);
return $retval;
}
/**
* Test if error.
*
* @return boolean If true then error.
*/
function isError() {
return sizeof($this->errors) > 0;
}
}
?>