Show:

File: platform/classes/Q/Config.php

<?php

/**
 * @module Q
 */
/**
 * Holds the entire Qbix configuration
 * @class Q_Config
 */
class Q_Config
{
	/**
	 * Loads a configuration file
	 * @method load
	 * @static
	 * @param {string} $filename The filename of the file to load.
	 * @param {boolean} $ignoreCache=false
	 *  Defaults to false. If true, then this function ignores
	 *  the cached value, if any, and attempts to search
	 *  for the file. It will cache the new value.
	 * @return {boolean} Returns true if saved, otherwise false.
	 */
	static function load(
	 $filename,
	 $ignoreCache = false)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Saves the configuration to a file
	 * @method save
	 * @static
	 * @param {string} $file The file to save into
	 * @param {array} [$array_path=array()] Array of keys identifying the path of the config subtree to save
	 * @return {boolean} Returns true if saved, otherwise false.
	 */
	static function save(
	 $filename, 
	 $array_path = array(), $prefix_path = null)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Gets the array of all parameters
	 * @method getAll
	 * @static
	 * @return {array}
	 */
	static function getAll ()
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Gets the value of a configuration field
	 * @method get
	 * @static
	 * @param {string} $key1 The name of the first key in the configuration path
	 * @param {string} $key2 Optional. The name of the second key in the configuration path.
	 *  You can actually pass as many keys as you need,
	 *  delving deeper and deeper into the configuration structure.
	 *  All but the second-to-last parameter are interpreted as keys.
	 * @param {mixed} $default The last parameter should not be omitted,
	 *  and contains the default value to return in case
	 *  the requested configuration field was not indicated.
	 */
	static function get(
	 $key1,
	 $default)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Sets the value of a configuration field
	 * @method set
	 * @static
	 * @param {string} $key1 The name of the first key in the configuration path
	 * @param {string} $key2 Optional. The name of the second key in the configuration path.
	 *  You can actually pass as many keys as you need,
	 *  delving deeper and deeper into the configuration structure.
	 *  All but the second-to-last parameter are interpreted as keys.
	 * @param {mixed} $value The last parameter should not be omitted,
	 *  and contains the value to set the field to.
	 */
	static function set(
	 $key1,
	 $value)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Clears the value of a configuration field, possibly deep inside the array
	 * @method clear
	 * @static
	 * @param {string} $key1 The name of the first key in the configuration path
	 * @param {string} $key2 Optional. The name of the second key in the configuration path.
	 *  You can actually pass as many keys as you need,
	 *  delving deeper and deeper into the configuration structure.
	 *  All but the second-to-last parameter are interpreted as keys.
	 */
	static function clear(
	 $key1)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Merges parameters over the top of existing parameters
	 * @method merge
	 * @static
	 * @param {array|Q_Tree} $second The array or Q_Tree to merge on top of the existing one
	 */
	static function merge ($second)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Gets the value of a configuration field. If it is null or not set,
	 * throws an exception. Otherwise, it is guaranteed to return a non-null value.
	 * @method expect
	 * @static
	 * @param {string} $key1 The name of the first key in the configuration path
	 * @param {string} $key2 Optional. The name of the second key in the configuration path.
	 *  You can actually pass as many keys as you need,
	 *  delving deeper and deeper into the configuration structure.
	 *  All but the second-to-last parameter are interpreted as keys.
	 * @return {mixed} Only returns non-null values
	 * @throws {Q_Exception_MissingConfig} May throw an exception if the config field is missing.
	 */
	static function expect(
		$key1)
	{
		$args = func_get_args();
		if (!isset(self::$tree)) {
			self::$tree = new Q_Tree;
		}
		return call_user_func_array(array(self::$tree, __FUNCTION__), $args);
	}
	
	/**
	 * Check for config server url and return it if found
	 * @method serverInfo
	 * @static
	 * @return {mixed} Config server information or false if config server is not defined
	 * or points to this server
	 */
	static private function serverInfo() {
		$cs = Q_Config::get('Q', 'internal', 'configServer', false);
		if ($cs && isset($cs['url'])) {
			$url = $cs['url'];
		} else $cs = false;
		return $cs;
	}

	/**
	 * Get contents of config file
	 * Config file is searched in APP_DIR/files forder. If config server url is defined
	 * the filename is searched on config server
	 * @method getFromServer
	 * @static
	 * @param {string} $filename The name of the config file. If config server is defined, file is got from there
	 * @return {array} The loaded tree
	 */
	static function getFromServer($filename) {
		if ($cs = self::serverInfo()) {
			// check Q_Cache and if set - use it
			// update class cache as it is not set
			$arr = Q_Cache::get("Q_Config\t$filename");
			if (isset($arr)) {
				$tree = new Q_Tree();
				$tree->merge($arr);
				return $tree->getAll();
			}

			// request config server
			if (empty($cs['url'])) {
				return;
			}
			if (!empty($cs['internal'])) {
				// query "internal" Qbix server
				$return = Q_Utils::queryInternal(
					'Q/Config',
					array('Q/method' => 'get', 'filename' => $filename),
					$cs['url']);
			} else {
				// query "external" Qbix server
				$return = Q_Utils::queryExternal(
					'Q/Config',
					array('Q/method' => 'get', 'filename' => $filename),
					$cs['url']);
			}
			Q_Cache::set("Q_Config\t$filename", $return);
			return $return;
		}
		// take local file, return empty tree if file does not exists
		$tree = new Q_Tree();
		if (defined('APP_DIR')) {
			$filename = APP_DIR.DS.'files'.DS.$filename;
		} else {
			throw new Q_Exception("'APP_DIR' is not defined");
		}
		$tree->load($filename);
		return $tree->getAll();
	}

	/**
	 * Modify a config file by merging over new data
	 * Config file is searched in APP_DIR/files forder. If config server url is defined
	 * the filename is searched on config server
	 * @method setOnServer
	 * @static
	 * @param {string} $filename The name of the config file. If config server is defined, file is changed there
	 * @param {array} $data The data to merge to the file
	 * @param {boolean} [$clear=false] Weather data shall be merged over or cleared and set
	 * @return {boolean} Wheather data was successfuly merged in
	 */
	static function setOnServer($filename, $data, $clear = false) {
		if (!is_array($data)) {
			throw new Q_Exception_WrongType(array('field' => 'data', 'type' => 'array'));
		}
		if (is_string($clear)) $clear = json_decode($clear);
		if ($cs = self::serverInfo()) {
			// request config server
			if (!empty($cs['url'])) {
				if (!empty($cs['internal'])) {
					// query "internal" Qbix server
					return Q_Utils::queryInternal(
						'Q/Config',
						array(
							'Q/method' => 'set',
							'filename' => $filename,
							'data' => $data,
							'clear' => $clear),
						$cs['url']);
				} else {
					// query "external" Qbix server
					return Q_Utils::queryExternal(
						'Q/Config',
						array(
							'Q/method' => 'set',
							'filename' => $filename,
							'data' => $data,
							'clear' => $clear),
						$cs['url']);
				}
			}
		}
		// save local file, return empty tree if file does not exists
		$tree = new Q_Tree();
		if (defined('APP_DIR')) {
			$filename = APP_DIR.DS.'files'.DS.$filename;
		} else {
			throw new Q_Exception("'APP_DIR' is not defined");
		}
		if (!file_exists($filename)) {
			$dir = dirname(str_replace('/', DS, $filename));
			if (!is_dir($dir)) {
				$mask = umask(Q_Config::get('Q', 'internal', 'umask', 0000));
				if (!mkdir($dir, 0777, true)) return false;
				umask($mask);
			} elseif (!is_writable($dir)) return false;
		} elseif (!$clear) $tree->load($filename);
		$tree->merge($data);
		return $tree->save($filename);
	}

	/**
	 * Modify a config file by clearing some data
	 * Config file is searched in APP_DIR/files forder. If config server url is defined
	 * the filename is searched on config server
	 * @method clearOnServer
	 * @static
	 * @param {string} $filename The name of the config file. If config server is defined, file is changed there
	 * @param {string|array} [$args=null] OA key or an array of keys for traversing the tree.
	 *	If keys are not supplied the file is cleared
	 *	If all-but-last keys point to plain array, last key is interpreted as a member
	 *	of that array and only this array member is removed
	 *	If all-but-last keys point to associative array (A) and last key is plain array (B)
	 *	all keys from array A which are in array B are unset
	 * @param {boolean} [$noSave=false] Weather result shall be returned or saved. Shall be of type boolean
	 * @return {boolean} Wheather data was successfuly cleared. If some key does not exist still true
	 * @throws {Q_Exception}
	 */
	static function clearOnServer($filename, $args = null, $noSave = false) {
		if (!isset($args) || $args === 'null') $args = array();
		if (is_string($args)) $args = array($args);
		if (is_string($noSave)) $noSave = json_decode($noSave);
		$noSave = !!$noSave;
		if ($cs = self::serverInfo()) {
			// request config server
			if (!empty($cs['url'])) {
				if (!empty($cs['internal'])) {
					// query "internal" Qbix server
					return Q_Utils::queryInternal(
						'Q/Config',
						array(
							'Q/method' => 'clear',
							'filename' => $filename,
							'args' => $args,
							'noSave' => $noSave),
						$cs['url']);
				} else {
					// query "external" Qbix server
					return Q_Utils::queryExternal(
						'Q/Config',
						array(
							'Q/method' => 'clear',
							'filename' => $filename,
							'args' => $args,
							'noSave' => $noSave),
						$cs['url']);
				}
			}
		}
		// modify local file
		if (defined('APP_DIR')) {
			$filename = Q::realPath(APP_DIR.DS.'files'.DS.$filename);
		} else {
			throw new Q_Exception("'APP_DIR' is not defined");
		}
		if (!$filename) return true;
		$tree = new Q_Tree();
		if (count($args)) {
			$tree->load($filename); // if not loaded we consider three empty
			if (count($args) > 1) 
				$last = call_user_func_array(array($tree, "get"), $args);
			else $last = $tree->getAll();
			if (is_array($last)) {
				if (array_keys($last) === range(0, count($last)-1)) {
					// it's plain array and we remove it's member
					$search = array_pop($args);
					if (!is_array($search)) $search = array($search);
					foreach ($search as $value) {
						$keys = array_keys($last, $value);
						for ($deleted=0, $i=0; $i<count($keys); $i++) {
							array_splice($last, $keys[$i]-($deleted++), 1);
						}
					}
					call_user_func_array(array($tree, "clear"), $args);
					if (count($last)) {
						array_push($args, $last);
						call_user_func_array(array($tree, "set"), $args);
					}
				} else {
					// $last is associative array
					$search = array_pop($args);
					if (!is_array($search)) $search = array($search);
					foreach ($search as $value) {
						call_user_func_array(array($tree, "clear"), array_merge($args, array($value)));
					}
				}
			}
		} else $tree = new Q_Tree();
		return $noSave ? $tree->getAll() : $tree->save($filename);
	}

	/**
	 * @property $tree {Q_Tree}
	 * @protected
	 * @static
	 */
	static protected $tree;
	/**
	 * @property $cache {mixed}
	 * @static
	 */
	static $cache = false; // for queries
}