<?php

	class Config
	{
		var $data = array();
		var $filename = '';
		var $error = '';
		var $cfgPrefix = '$GLOBALS';

		var $unquoted = array (
			'true',
			'false',
		);

		/**
		* Constructor - allows you to specify a file for subsequant operations
		*
		* @param string $file The file to use by default
		* @return void
		*/
		function Config($file=null)
		{
			if ($file !== null) {
				$this->filename = $file;
			}
		}


		/**
		* Load a config file
		*
		* @param string $file The file to load
		* @return bool true on successful load
		*/
		function Load($file='')
		{
			// If a filename was used at construction time we don't need to
			// explicitly require one here. We can reuse the one previously
			// passed in to us
			if (empty($file) && !empty($this->filename)) {
				$file = $this->filename;
			}

			if (!file_exists($file)) {
				$this->error = 'File ['.$file."] doesn't exist";
				return false;
			}

			$lines = file($file);
			foreach ($lines as $lineNumber => $line) {
				list($key, $val) = $this->ParseLine($line);
				if (!empty($key)) {
					if (in_array($val, $this->unquoted) || is_numeric($val)) {
						$this->data[$key] = $val;
					} else {
						$this->data[$key] = stripslashes($val);
					}
				}
			}
			return true;
		}


		/**
		* Save a config to disk
		*
		* @param string $file The filename to save to
		* @return bool True on successful save
		*/
		function Save($file='')
		{
			// If a filename was used at construction time we don't need to
			// explicitly require one here. We can reuse the one previously
			// passed in to us
			if (empty($file) && !empty($this->filename)) {
				$file = $this->filename;
			}

			if (!file_exists($file)) {
				$this->error = 'File ['.$file."] doesn't exist";
				return false;
			}

			if (empty($this->data)) {
				$this->error = 'No data to save';
				return false;
			}

			$output = '<?php'."\n";

			foreach ($this->data as $key => $val) {
				$output .= "\t".$this->cfgPrefix.'["'.$key.'"] = '.$val.";\n";
			}
			
			$output .= '?>';

			$fp = @fopen($file, "wb+");
			if ($fp) {
				if (fwrite($fp, $output, strlen($output)) === false) {
					$this->error = "Could not output new config to disk";
					return false;
				}
				fclose($fp);
			} else {
				$this->error = "The file couldn't be opened for writing";
				return false;
			}
			
			return true;
		}

		/**
		* Update
		* Update a config setting
		*
		* @param $key The name of the config setting
		* @param $val The value to set it to
		* @param $strict if the setting doesn't already exist then it won't
		* create it unless this is set to false
		*
		* @return bool Was the update a success ?
		*/
		function Update($key, $val, $strict=true)
		{
			// We can't update an unknown variable
			if (empty($key)) {
				$this->error = 'The variable ['.$key.'] is empty';
				return false;
			}

			// If the data hasn't been loaded but we know the file to work on
			// Load the file automatically
			if (empty($this->data) && !empty($this->filename)) {
				$this->Load();
			}

			// If we are strict about updating only (i.e. not creating a new
			// entry when it doesn't already exist) then if the key isn't
			// already setup, stop
			if ($strict && !isset($this->data[$key])) {
				$this->error = 'Strict mode is on and the key ['.$key.'] is not loaded yet';
				return false;
			}

			if (in_array($val, $this->unquoted) || is_numeric($val)) {
				$this->data[$key] = $val;
			} else {
				$this->data[$key] = "'".addslashes($val)."'";
			}
			return true;
		}

		/**
		* ParseLine
		* Parse a line in a config file and work out if it has any valid data
		*
		* @param string $line the contents of the line to parse
		*
		* @return mixed an array with structure array(var, val) or false on error
		*/
		function ParseLine($line)
		{
			$data = array();
			if (strpos($line, $this->cfgPrefix.'[')) {
				$parts = explode('=', $line, 2);
				$parts = array_map('trim', $parts);
			} else {
				$this->error = 'The line ['.$line.'] does not contain $GLOBALS[';
				return false;
			}
			$data[0] = $this->ParseVar($parts[0]);
			$data[1] = $this->ParseVal($parts[1]);
			return $data;

		}

		/**
		* ParseVar
		* Parse the value of a config variable name from a string
		*
		* @param $var The string to parse
		*
		* @return mixed the name of the key extracted from $var of false on error
		*/
		function ParseVar($var)
		{
			$len = strlen($this->cfgPrefix)+1;

			if (substr($var, 0, $len) == $this->cfgPrefix.'[') {
				$secondQuote = strpos($var, "'", $len+1);
				if ($secondQuote === false) {
					$secondQuote = strpos($var, '"', $len+1);
				}

				if ($secondQuote === false) {
					// If we havn't found a single or double quote stop
					$this->error = 'Can not find a quote in the key string';
					return false;
				} else {
					return substr($var, $len+1, $secondQuote-$len-1);
				}
			} else {
				// If the string doesnt start with $GLOBALS[ stop
				$this->error = 'The var ['.$var.'] does not start with $GLOBALS[';
				return false;
			}
		}

		/**
		* ParseVal
		* Parse the value of a config setting from a string
		*
		* @param string $val The value to parse
		*
		* @return string The value of the setting
		*/
		function ParseVal($val)
		{
			if (substr($val, -1) == ';') {
				return substr($val, 0, -1);
			} else {
				return $val;
			}
		}
	}
?>
