Location: PHPKode > projects > V-webmail > includes/cachedConfig.php
<?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;
	}
}
?>
Return current item: V-webmail