From a65e4e91a14d933b5570673ebe8da917fd937c96 Mon Sep 17 00:00:00 2001 From: Christian Stocker Date: Mon, 2 Jul 2001 09:14:28 +0000 Subject: [PATCH] Moved Config-Package from Experimental to main-directory, since noone complained about it ;) --- pear/Config.php | 329 +++++++++++++++++++++++ pear/Config/Container.php | 171 ++++++++++++ pear/Config/Container/IniFile.php | 219 +++++++++++++++ pear/Config/Container/db.php | 175 ++++++++++++ pear/Config/Container/xml.php | 154 +++++++++++ pear/Config/README.Config | 428 ++++++++++++++++++++++++++++++ 6 files changed, 1476 insertions(+) create mode 100644 pear/Config.php create mode 100644 pear/Config/Container.php create mode 100644 pear/Config/Container/IniFile.php create mode 100644 pear/Config/Container/db.php create mode 100644 pear/Config/Container/xml.php create mode 100644 pear/Config/README.Config diff --git a/pear/Config.php b/pear/Config.php new file mode 100644 index 0000000000..e37be821be --- /dev/null +++ b/pear/Config.php @@ -0,0 +1,329 @@ + | +// | Christian Stocker | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once( "PEAR.php") ; + +/** +* Partly implementation of the Config-Interface-API +* +* This class implements a part of the API for working on configuration data +* ConfigDefault implements getValues(), getBlocks(), getValue(), setValue() +* The data is internally saved in a nested array. +* +* Example: +* include("Config.php"); +* $i = new Config("IniFile"); +* $i -> parseInput( "yourfile.ini"); +* $ret = $i->getValues('/section'); +* +* +* @author Alexander Merz +* @access public +* @version $Id$ +* @package Config +*/ + +class Config { + + /** + * contains the data source given by parseInput + * @var string + */ + var $datasrc ; + + /** + * contains the internal data structure + * @var array + */ + var $data = array() ; + + /** + * Constructor + * + * requires the type of the data container, if the container needs + * special parameters during creation, set them with $storage_options + * + * @access public + * @param string $storage_driver type of container + * @param string $storage_options parameter for container constructor + */ + function Config($storage_driver,$storage_options = "") + { + + $storage_class = 'Config_Container_' . $storage_driver; + $storage_classfile = 'Config/Container/' . $storage_driver . '.php'; + + include_once $storage_classfile; + $this->container = new $storage_class($storage_options); + } // end func Config() + + + + /** + * returns all key-value-pairs of the given block + * + * If the block not exists, a PEAR_Error will returned, else + * a hash: $array["key"] = value + * + * @access public + * @param string $block block path + * @return array returns a hash containing all values, but a PEAR_Error if fails + */ + + function getValues( $block = "/") + { + if( !empty( $this -> data ) ) + { + // if leading slash was forgotten... + if( "/" != substr( $block, 0, 1) ) + { + $block = "/".$block ; + } + if( isset( $this -> data[ $block ] ) ) + { + if( is_array( $this -> data[ $block ] ) ) + { + $ret = $this -> data[ $block ] ; + } + else + { + $ret = false ; + } + } + else + { + $ret = new PEAR_Error("Block path '".$block."' doesn't exists! Request couldn't be answered.", 12, PEAR_ERROR_RETURN, null, null ); + } + } + else + { + $ret = new PEAR_Error("No internal data! Request couldn't be answered.", 11, PEAR_ERROR_RETURN, null, null ); + } + + return $ret ; + } // end func getValues + + /** + * returns all blocks of the given block + * + * If the block not exists, a PEAR_Error will returned, else + * a array containing all child blocks + * + * @access public + * @param string $block block path + * @return array returns a array containing all values, or a PEAR_Error, if fails + */ + + function getBlocks( $block = "/") + { + if( !empty( $this -> data ) ) + { + // if leading slash was forgotten... + if( "/" != substr( $block, 0, 1) ) + { + $block = "/".$block ; + } + $ret = array() ; + foreach( $this -> data as $key => $value) + { + $key = $key."/" ; + if( $block == substr( $key, 0, strlen( $block ) ) ) + { + array_push ( $ret, trim( substr( $key, strlen( $block ), strpos( substr( $key, strlen( $block ) ), "/" ) ) ) ) ; + } + } + } + else + { + $ret = new PEAR_Error("No internal data! Request couldn't be answered.", 21, PEAR_ERROR_RETURN, null, null ); + } + + return $ret ; + } // end func getBlocks + + /** + * sets the value of the key of the given block + * + * If the block or the key not exists, both will be created. + * The value will be returned. + * + * @access public + * @param string $block block path + * @param string $key key to set + * @param string $value value for the key + * @return mixed type depends on $value + * @see getValue() + */ + + function setValue( $block = "/", $key, $value = "") + { + // if leading slash was forgotten... + if( "/" != substr( $block, 0, 1) ) + { + $block = "/".$block ; + } + // check for existing block and key + if( !isset ( $this -> data[ $block ] ) ) + { + $this->data[ $block ] = array() ; + } + $kvp = $this -> data[ $block ] ; + $kvp[ $key ] = $value ; + $this -> data[ $block ] = $kvp ; + $ret = $value ; + + return $ret ; + } // end func setValue + + /** + * return the value of the key of the given block + * + * If the block or the key not exists, both will be created and + * sets on the default. + * The value or if not exists the default will be returned. + * + * @access public + * @param string $block block path + * @param string $key key to set + * @param string $default default value for the key + * @return mixed type depends of the value + */ + + function getValue( $block = "/", $key, $default = "") + { + // if leading slash was forgotten... + if( "/" != substr( $block, 0, 1) ) + { + $block = "/".$block ; + } + // check for existing block and key + $values = $this -> getValues( $block ) ; + if( PEAR::isError($values) or !in_array( $key, array_keys( $values) ) ) + { + $this -> setValue( $block, $key, $default) ; + $values = $this -> getValues( $block ) ; + } + $ret = $values[ $key ] ; + + return $ret ; + } // end func getValue + + /** + * parses the input of the given data source + * + * The format and required content of $datasrc depends of the implementation. + * If the implemention requires additional data, for example a comment char, it have to + * deliver in a hash as second argument. + * + * @access public + * @param string $files Name of the datasource to parse + * @param array $feature Contains a hash of features depending on the implentation + * @return mixed returns a PEAR_ERROR, if error occurs + */ + + function parseInput ($files ,$feature = array() ) + { + if (is_array($files)) { + $totaldata = array(); + foreach ($files as $datasrc) + { + $this->container->parseInput($datasrc,$feature); + $totaldata = $this->array_merge_clobber($totaldata,$this->container->data); + unset ($this->data); + $this->datasrc = $datasrc; + } + $this->data = $totaldata; + } + else + { + $this->container->parseInput($files,$feature); + $this->data = $this->container->data; + $this->datasrc = $files; + } + + } // end func parseInput() + + + /** + * writes the data to the given data source or if not given to the datasource of parseInput + * If $datasrc was a array, the last file will used. + * + * See parseInput for $datasrc. If the second argument $preserve is true, the implementation + * should try to preserve the original format and data of the source except changed or added values. + * This mean to preserve for example comments in files or so. + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param boolean $preserve preserving behavior + * @return mixed returns PEAR_Error, if fails + * @see parseInput() + */ + function writeInput( $datasrc = "", $preserve = True ) + { + if( empty( $datasrc ) ) { + $datasrc = $this -> datasrc ; + } + $this->container->writeInput($datasrc,$preserve); + } + + + + //taken from kc@hireability.com at http://www.php.net/manual/en/function.array-merge-recursive.php + /** + * There seemed to be no built in function that would merge two arrays recursively and clobber + * any existing key/value pairs. Array_Merge() is not recursive, and array_merge_recursive + * seemed to give unsatisfactory results... it would append duplicate key/values. + * + * So here's a cross between array_merge and array_merge_recursive + * + * @param array first array to be merged + * @param array second array to be merged + * @return array merged array + * @acces private + */ + function array_merge_clobber($a1,$a2) + { + if(!is_array($a1) || !is_array($a2)) return false; + $newarray = $a1; + while (list($key, $val) = each($a2)) + { + if (is_array($val) && is_array($newarray[$key])) + { + $newarray[$key] = $this->array_merge_clobber($newarray[$key], $val); + } + else + { + $newarray[$key] = $val; + } + } + return $newarray; + } + + + +}; // end class Config + + + + + + + +?> diff --git a/pear/Config/Container.php b/pear/Config/Container.php new file mode 100644 index 0000000000..b62cc37187 --- /dev/null +++ b/pear/Config/Container.php @@ -0,0 +1,171 @@ + | +// | Christian Stocker | +// +---------------------------------------------------------------------+ +// +// $Id$ + +/** +* Interface for Config-Classes +* +* This interface provides a API for configuration data +* +* @author Alexander Merz +* @access public +* @version $Id$ +* @package Config +*/ + + + +class Config_Container { + + /** + * parses the input of the given data source + * + * The format and required content of $datasrc depends of the implementation. + * If the implemention requires additional data, for example a comment char, it have to + * deliver in a hash as second argument. + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param array $feature Contains a hash of features depending on the implentation + * @return mixed returns a PEAR_ERROR, if error occurs + */ + + function parseInput( $datasrc = "", $feature = array() ) + { + return new PEAR_Error("parseInput not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func parseInput + + /** + * writes the data to the given data source or if not given to the datasource of parseInput + * + * See parseInput for $datasrc. If the second argument $preserve is true, the implementation + * should try to preserve the original format and data of the source except changed or added values. + * This mean to preserve for example comments in files or so. + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param boolean $preserve preserving behavior + * @return mixed returns PEAR_Error, if fails + * @see parseInput() + */ + + function writeInput( $datasrc = "", $preserve = true ) + { + return new PEAR_Error("writeInput not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func writeInput + + /** + * returns all key-value-pairs of the given block + * + * The implementation should returns only the pairs of the given + * block, no names of subblocks. + * Blocks should be looks like this "/rootblock/child_block/grandchild_block/..." + * Take care of the leading slash - this is a "root"-slash according to the "root"-dir + * under Unix. If a config data source contains no blocks or using of blocks makes no sense + * the implementation have to support at least the root-slash. + * + * @access public + * @param string $block block path + * @return array returns a hash containing all values, but a PEAR_Error if fails + */ + + function getValues( $block ) + { + return new PEAR_Error("getValues not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func getValues + + /** + * returns the name of all childblocks + * + * The implementation returns the name of all childblocks in the given block as a array. + * If a childblock contains also childblocks the value of the block have to be also a + * array containing all grandchild-blocks + * + * @access public + * @param string $block block path + * @return array returns a array containing all values, or a PEAR_Error, if fails + * @see getValues() + */ + + function getBlocks( $block ) + { + return new PEAR_Error("getBlocks not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func getBlocks + + /** + * returns the value of the key in the given block + * + * The implementation returns the value of the key in the given block. If the key or the block not exist + * the implementation should return the default-value and create the block with the key and the default-value. + * + * @access public + * @param string $block block path + * @param string $key key + * @param string $default default-value + * @return mixed type depends of the value + * @see getValues() + */ + + function getValue( $block, $key, $default = "" ) + { + return new PEAR_Error("getValue not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func getValue + + /** + * sets the value of the key in the given block + * + * The implementation sets the value of the key in the given block and returns value. + * If the key or the block not exist, the implementation should create the block with + * the key and the value. + * + * @access public + * @param string $block block path + * @param string $key key + * @param string $value value to set + * @return mixed type depends on $value + * @see getValue() + */ + + function setValue( $block, $key, $value ) + { + return new PEAR_Error("setValue not implemented", 1, PEAR_ERROR_RETURN, null, + null ); + } // end func setValue + + /** + * Imports the requested datafields as object variables if allowed + * + * @param array List of fields to be imported as object variables + * @param array List of allowed datafields + */ + + function setFeatures($requested, $allowed) { + foreach ($allowed as $k => $field) + if (isset($requested[$field])) + $this->feature[$field] = $requested[$field]; + + } // end func setFeatures +} + +?> diff --git a/pear/Config/Container/IniFile.php b/pear/Config/Container/IniFile.php new file mode 100644 index 0000000000..02b76a8797 --- /dev/null +++ b/pear/Config/Container/IniFile.php @@ -0,0 +1,219 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once( "Config/Container.php" ) ; + +/** +* Config-API-Implemtentation for Ini-Files +* +* This class implements the Config-API based on ConfigDefault +* +* @author Alexander Merz +* @access public +* @version $Id$ +* @package Config +*/ + +class Config_Container_IniFile extends Config_Container { + +/** +* contains the features given by parseInput +* @var array +* @see parseInput() +*/ +var $feature = array() ; + +/** +* parses the input of the given data source +* +* The Data Source can be a string with the file or a array of strings with the files to read, +* so datasrc requires a existing file. +* The feature-array have to contain the comment char array("cc" => Comment char) +* +* @access public +* @param string $datasrc Name of the datasource to parse +* @param array $feature Contains a hash of features +* @return mixed returns a PEAR_ERROR, if error occurs +*/ + +function parseInput( $datasrc = "", $feature = array( "cc" => ";") ) +{ + + // Checking if $datasrc is a array, then call parseInput with + // each file + if( is_array( $datasrc) ) { + foreach( $datasrc as $file ) { + $ret = $this -> parseInput( $file, $feature ) ; + if( PEAR::isError( $ret) ) { + return $ret ; + } + } + } + + + $this -> datasrc = $datasrc ; + $this -> feature = $feature ; + if( file_exists( $datasrc ) ) + { + $lines = file( $datasrc ) ; + $block = "/" ; + $zeilennr = 0 ; + foreach( $lines as $line) + { + $value_found = "" ; + $kkey_found = "" ; + + $line = trim( $line) ; + // is it a blank line? + if( empty( $line) ) + { + $found = 0 ; + } + // checking for data + else + { + $char = substr( $line, 0, 1) ; + + // a comment? + if( $char == $this -> feature["cc"] ) + { + $found = 0 ; + } + // a block? + elseif( $char == '[' ) + { + $found = 1 ; + $block_found = substr( $line, 1, strpos( substr( $line, 1 ), ']' ) ) ; + } + // must be a kvp + else + { + $key = substr( $line, 0, strpos( $line, '=') ) ; + if( !$key ) + { + return new PEAR_Error( "Line ".$zeilennr." in '".$datasrc."' seems to be a comment, but comment char is missing!", 41, PEAR_ERROR_RETURN, null, null ); + } + $value = (string)trim( (string)substr( (string)$line, strpos( (string)$line, '=') ) ) ; + $value = (string)trim( (string)substr( (string)$value, 1 ) ); + + // checking for double quoted string + if( '"' == substr( $value, 0, 1) ) + { + // value = all until next " + $value_found = substr( $value, 0, strpos( substr( $value, 1 ), '"' ) + 1) ; + $value_found = substr( $value_found, 1, strlen( $value_found) ) ; + } + else + { + // value = all until next space, eol or comment + preg_match( "/([^\s".$this -> feature["cc"]."]*)[\s]*[".$this -> feature["cc"]."]*[\s]*[\S]*[\s]*/", trim( $value ), $match) ; + $value_found = $match[1] ; + } + + $key_found = trim( $key ) ; + $found = 2 ; + } + } + + // creating the array + switch( $found ) + { + case 1 : + $block = '/'.$block_found ; + break ; + case 2 : + + $aadd = $this -> data[ $block ] ; + $aadd[ $key_found ] = (string)trim ((string)$value_found ) ; + $this -> data[$block] = $aadd ; + break ; + case 0 : + default : + break ; + } + $zeilennr++ ; + } + } + else + { + return new PEAR_Error( "File '".$datasrc."' doesn't exists!", 31, PEAR_ERROR_RETURN, null, null ); + } + +} // end func parseInput + +/** +* writes the data to the given data source or if not given to the datasource of parseInput +* If $datasrc was a array, the last file will used. +* +* See parseInput for $datasrc. If the second argument $preserve is true, the implementation +* should try to preserve the original format and data of the source except changed or added values. +* This mean to preserve for example comments in files or so. +* +* @access public +* @param string $datasrc Name of the datasource to parse +* @param boolean $preserve preserving behavior +* @return object PEAR_Error +* @see parseInput() +*/ + +function writeInput( $datasrc = "", $preserve = true ) +{ + + if( empty( $datasrc ) ) { + $datasrc = $this -> datasrc ; + } + elseif( !file_exists( $datasrc )) { + return new PEAR_Error("File '$datasrc' doesn't exist", 41, PEAR_ERROR_RETURN, null, + null ); + } + + if( $preserve ) { + return new PEAR_Error("Preserving not supported", 49, PEAR_ERROR_RETURN, null, + null ); + } + else { + $fh = fopen( $datasrc, "w") ; + if( !$fh) { + return new PEAR_Error("Couldn't open '$datasrc' for writing", 42, PEAR_ERROR_RETURN, null, + null ); + } + else { + foreach( $this -> data as $block => $blockkv ) { + $block = substr( $block, 1) ; + fwrite( $fh, "[$block]\n" ) ; + foreach( $blockkv as $key => $value ) { + if( strpos ( $value, ' ' )) { + $value = '"'.$value.'"' ; + } + fwrite( $fh, "$key = $value\n" ) ; + } + fwrite( $fh, "\n" ) ; + } + fclose( $fh) ; + } + + } +} // end func writeInput + + +}; // end class Config_IniFile + + + +?> diff --git a/pear/Config/Container/db.php b/pear/Config/Container/db.php new file mode 100644 index 0000000000..049445bb77 --- /dev/null +++ b/pear/Config/Container/db.php @@ -0,0 +1,175 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ + +require_once( "Config/Container.php" ) ; + +/** +* Config-API-Implemtentation for DB-Ini-Entries +* +* This class implements the Config-API based on ConfigDefault +* +* The Table structure should be as follows: +* +* CREATE TABLE config ( +* datasrc varchar(50) NOT NULL, +* block varchar(50) NOT NULL, +* name varchar(50) NOT NULL, +* value varchar(50) +* ); +* +* You can name the Table other than config, but you have to supply then +* this name in the $feature->array +* +* @author Christian Stocker +* @access public +* @version $Id$ +* @package Config +*/ + +class Config_Container_db extends Config_Container { + + + /** + * contains the features given by parseInput + * @var array + * @see parseInput() + */ + var $feature = array("Table" => "config") ; + + /** + * Constructor of the class + * + * Connects to the DB via the PEAR::DB-class + * + * @param $dsn string with PEAR::DB "data source name" or object DB object + */ + + function Config_Container_db ($dsn ) { + + //if dsn is a string, then we have to connect to the db + if (is_string($dsn)) + { + include_once ("DB.php"); + $this->db = DB::Connect($dsn); + if (DB::isError($db)) + { + print "The given dsn was not valid in file ".__FILE__." at line ".__LINE__."
\n"; + return new DB_Error($db->code,PEAR_ERROR_DIE); + } + + } + + //if it's an object, it could be a db_object. + elseif (is_object($dsn) && DB::isError($dsn)) + { + print "The given param was not valid in file ".__FILE__." at line ".__LINE__."
\n"; + return new DB_Error($dsn->code,PEAR_ERROR_DIE); + } + + // if parent class is db_common, then it's already a connected identifier + elseif (get_parent_class($dsn) == "db_common") + { + $this->db = $dsn; + } + + else { + return new PEAR_Error( "The given dsn ($dsn) was not valid in file ".__FILE__." at line ".__LINE__, 41, PEAR_ERROR_RETURN, null, null ); + } + + + } + + /** + * parses the input of the given data source + * + * The Data Source can be a string with field-name of the datasrc field in the db + * + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param array $feature Contains a hash of features + * @return mixed returns a DB_ERROR, if error occurs + */ + + function parseInput( $datasrc = "", $feature = array( ) ) + { + $this->setFeatures($feature, array_merge($this->allowed_options,array("Table"))); + + $query = "SELECT block,name,value from ".$this->feature["Table"]." where datasrc = '$datasrc'"; + $res = $this->db->query($query); + if (DB::isError($res)) + { + return new DB_Error($dsn->code,PEAR_ERROR_DIE); + } + else + { + while ($entry = $res->FetchRow(DB_FETCHMODE_ASSOC)) + { + $this->data[$entry[block]][$entry[name]] = $entry[value]; + } + } + + + } + + + /** + * writes the data to the given data source or if not given to the datasource of parseInput + * If $datasrc was a array, the last file will used. + * + * See parseInput for $datasrc. If the second argument $preserve is true, the implementation + * should try to preserve the original format and data of the source except changed or added values. + * This mean to preserve for example comments in files or so. + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param boolean $preserve preserving behavior + * @return mixed returns a DB_ERROR, if error occurs + * @see parseInput() + */ + + function writeInput( $datasrc = "", $preserve = True ) + { + $query = "delete from ".$this->feature["Table"]." where datasrc = '$datasrc'"; + $res = $this->db->query($query); + if (DB::isError($res)) + { + return new DB_Error($dsn->code,PEAR_ERROR_DIE); + } + + + foreach ($this->data as $block => $blockarray) + { + + foreach ($blockarray as $name => $value) + { + $query = "insert into ".$this->feature["Table"]." (datasrc,block,name,value) VALUES + ('".addslashes($datasrc)."','".addslashes($block)."','".addslashes($name)."','".addslashes($value)."')"; + + $res = $this->db->query($query); + if (DB::isError($res)) + { + return new DB_Error($dsn->code,PEAR_ERROR_DIE); + } + + } + } + } +} +?> \ No newline at end of file diff --git a/pear/Config/Container/xml.php b/pear/Config/Container/xml.php new file mode 100644 index 0000000000..4288f989aa --- /dev/null +++ b/pear/Config/Container/xml.php @@ -0,0 +1,154 @@ + | +// | Alexander Merz | +//+----------------------------------------------------------------------+ +// + +require_once( "Config/Container.php" ) ; + +/** +* Config-API-Implemtentation for XML-Files +* +* This class implements the Config-API based on ConfigDefault +* +* @author Christian Stocker +* @access public +* @version Config_xml.php, 2000/04/16 +* @package Config +*/ + + +class Config_Container_xml extends Config_Container { + + /** + * contains the features given by parseInput + * @var array + * @see parseInput() + */ + var $feature = array ("IncludeContent" => True, + "MasterAttribute" => False, + "IncludeMasterAttribute" => True, + "IncludeChildren" => True + ); + + /** + * parses the input of the given data source + * + * The Data Source is a file, so datasrc requires a existing file. + * The feature-array have to contain the comment char array("cc" => Comment char) + * + * @access public + * @param string $datasrc Name of the datasource to parse + * @param array $feature Contains a hash of features + * @return mixed returns a PEAR_ERROR, if error occurs + */ + + function parseInput( $datasrc = "",$feature = array() ) + { + + $this -> datasrc = $datasrc ; + $this->setFeatures($feature, array_merge($this->allowed_options, array('IncludeContent', 'MasterAttribute','IncludeMasterAttribute','IncludeChildren'))); + if( file_exists( $datasrc ) ) + { + $xml = xmldocfile($datasrc); + $root = domxml_root($xml); + $this->addAttributes($root); + $this->parseElement($root,"/".$root->name); + } + else + { + return new PEAR_Error( "File '".$datasrc."' doesn't + exists!", 31, PEAR_ERROR_RETURN, null, null ); + } + } // end func parseInput + + /** + * parses the input of the XML_ELEMENT_NODE into $this->data + * + * @access private + * @param object XML_ELEMENT_NODE $element + * @param string $parent xpath of parent ELEMENT_NODE + */ + + function parseElement ($element,$parent = "/") { + + foreach(domxml_children($element) as $tag => $value) + { + if (XML_ELEMENT_NODE == $value->type) + { + $this->addAttributes($value,$parent); + if (domxml_children($value)) + { + $this->parseElement($value,$parent."/".$value->name); + } + } + } + } + //end func parseElement + + /** + * ?? ask Christian + * + * @access private + * @param string $element the element to add perhaps? + * @param object I_dont_know $parent the parent element? + */ + + + function addAttributes($element,$parent="") + { + if ($parent=="") { + //this is only for the root element + $parentslash ="/"; + } + + if ($this->feature["IncludeChildren"] ) + { + $this->data["$parent"."$parentslash"]["children"][] = $element->name; + + } + if (($this->feature["IncludeContent"]|| $this->feature["MasterAttribute"] == "content") && $element->content) + { + if ($this->feature["MasterAttribute"] == "content") + { + $this->data["$parent"."$parentslash"][$element->name] =$element->content; + } + if ($this->feature["IncludeMasterAttribute"] || $this->feature["MasterAttribute"] != "content") + { + $this->data["$parent/".$element->name]["content"] =$element->content; + } + } + if (domxml_attributes($element) ) + { + foreach (domxml_attributes($element) as $attribute => + $attributeObject) + { + if ($this->feature["MasterAttribute"] && $attributeObject->name == $this->feature["MasterAttribute"]) + { + $this->data[$parent."$parentslash"][$element->name] = domxml_get_attribute($element,$attributeObject->name); + } + if ($this->feature["IncludeMasterAttribute"] || $attributeObject->name != $this->feature["MasterAttribute"]) + { + $this->data["$parent/".$element->name][$attributeObject->name] = domxml_get_attribute($element,$attributeObject->name); + } + } + } + } + //endfunc addAttributes +}; + + +?> diff --git a/pear/Config/README.Config b/pear/Config/README.Config new file mode 100644 index 0000000000..a4403fc19a --- /dev/null +++ b/pear/Config/README.Config @@ -0,0 +1,428 @@ +Documentation and Tutorial for PEAR-Config + +by Alexander Merz +$Id$ + +A first example + +For beginning we start with a useful script example: + +----- example.php ------ + + parseInput( $path.$file, $options) ; +$blocks = $conf -> getBlocks( "/" ) ; + +foreach( $blocks as $block ) { // read the k-v-p of every block + $data[$block] = $conf -> getValues( "/".$block ) ; +} + +// output +?> + + + Test for PEAR - Config + + + + " ; + foreach( $d as $k => $v ) { + echo "" ; + } + } + ?> +
Block: $d
$k$v
+ + +----- example.php ------ + +This script parses the php.ini and creates a table containing the name of all blocks and +the key-value-pairs of every block. + +Now, lets cut up the script. + + require_once( "config.php" ) ; + +First of all, to use the Config-API you have to include +"config.php". If you have a regular PHP installation including PEAR, there should be +no problem for PHP to find the file. Unfortunatly a large number of ISP didn't installed PEAR +or have a older installation, where Config isn't present. In this case, we have good news for +you. + +Config is "pure" PHP. This means you can download "config.php" and (important!) the +Config directory from cvs.php.net and copy the files to your webspace. Take care of using the +correct path to "config.php" in the require statement. Also, go this way, if you perhaps want +to use a newer version of Config. + + $conf = new Config( "IniFile") ; + +With this line you create a new instance of the Config class. The constructor requires the +declaration of the type of the container, which contains the configuration data. Check the +'Implementations' section in this documentation the see which containers are avaible at the +moment. + +"php.ini" is a typical example for a ini file and should be present on your system, if you have +installed PHP ( else this document should be a bit useless for you!?). So, to work with this +file, we have to say Config to parse this file. + + $conf -> parseInput( $path.$file, $options) ; + +Normaly this function reads the data container and writes the data into an internal data +structure. How to adress specific the container depends on the implemention, IniFile +requires an existing file. The second parameter contains a set of options. Which options exist +and/ or required depends on the type of the container. In this case, Ini files have often +comments, which should not be parsed, "php.ini" uses the semicolon to mark up the start of +a comment. + +At this point, we can start to accessing the configuration data. + +To accessing the data, you should know something about how Config 'structurize' the data. +Config distinguish three types of elements: keys, values and blocks. Take a look on "php.ini". + +The first 'key' is "engine". Keys are names of options and/or of data elements. To every key +belongs a 'value'. The value of the key "engine" could be "on" or "off". "on" or "off" are +data of the key. Take care, also missing of such data, ie. a empty string is a value. + +Often, ie. "php.ini", the data container contains a huge number of key-value pairs. Sometimes +it isn't possible to avoid two or more options with the same key name, ie. you want to save +the access data for databases with different usernames, passwords and tablenames. To separate +them, you can edge them in a 'block'. Another typical use of blocks is to summarize +key-values pairs which belong together in a block, as you can see in "php.ini". Especially, if +a data container should be editable be humans without a special programm, you should use blocks +to structurize the data. + +The data in "php.ini" is completly separate into blocks, so as the first access, we want the +names of the blocks. + + $blocks = $conf -> getBlocks( "/" ) ; + +getBlocks() returns an array containing all block names. You maybe wonder about the argument. +getBlocks requires a 'blockpath'. Config structurize the data as a tree comparable to a +file system. Think blocks as directories and key-value pairs as files. You can store a file +in the root dir ( "/" under Unix-like systems) or in a directory ( ie. ( "/usr/" ) or +subdirectories ( ie. "/usr/bin/" ). According to this, there are implementations which +support sub-blocks in blocks. + +IniFile doesn't support block nesting, only blocks in the first level, the 'root' level and +the root level is adressed with a single slash. + +Take care: If you want all sub blocks of ie. the block named "db_accessdata", the blockpath is +"/db_accessdata/" - don't forget the leading root slash! Also, if you need to address +key-value-pairs, as you can see later in this manual, which are not in a block, you have to +use "/" as blockpath. + +As the next we want all key-value pairs of every block. To get them we use + + $data[$block] = $conf -> getValues( "/".$block ) ; + +to recieve them. getValues() returns a hash with all key-value pairs in the given block path. +The key will used as hash key containing the value of the key, +ie: $data[ 'PHP' ][ 'engine' ] = "on". + +So thats all, what would be worthy to talk about the script! Now a little exercise for you: +You should able to rewrite the script to print out the content of every configuration data +container including key-value-pairs in subblocks. + +Getting and Setting + +getValues is a possibility to get all config data of a block. But often you need only +a few of them and/ or you want more influence oft the returned data. + +To only get the value of one key, you can use getValue(). getValue() requires the blockpath as +the first argument like dicussed above, as the next the name of the key as a string. +getValue() has a third optional parameter. If the key in the named block doesn't exists, the +key and if neccessary the block will be created and the third parameter will be assigned +to them and returned by getValue(). + +The counterpart of getValue() is setValue(). setValue() requires a blockpath, the name of the +key too, but as the third argument the value, which should be assigned to the key. The behavior +of setValue, if the key or the block not exists, is the same like getValue(). setValue() +returns the assigned value too. + +Saving changes + +If you changed values of keys or adding keys or blocks, this changes only influence the +internal data structure of Config. So changes will lost during finishing the script. +To save them permantly, you have to call writeInput(). writeInput has two optional +arguments. The first is the name of the data container, where the data should be saved. +If you left this part empty, writeInput() use the data container given in parseInput() as +destination. + +The second optional argument orders writeInput() to save the data preserving the +content and format of the original container, as good as possible, if the argument is true. +This option makes in the most cases only sense in file-based containers like IniFile. Normaly +configuration files contains ie. comments, a lot of whitespaces to make the files editable by +humans. This data will not be parsed into the internal data struture and so cannot be written +back to the container. If the preserving argument is true, writeInput() takes the original +container and tries to copy the content, and changes only different values and adds new keys and +blocks keeping the original file content including comments and whitespace. + +Implementations + +IniFile + +The container is a file with following format + +... +; comment +[blockname] ; comment +key=value ; comment +key="value with whitespaces; and the comment char" ; comment +... + +Comments are marked by a special starting comment char and can stay on a single line +or after a value, but not between a key-value assignment. + +Blocks are marked between brackets and the block name between them. There is no block end +markup. A block ist finish by the end of the file or a new block markup. Block nesting is not +possible. + +Only one key-value assignment per line is allowed. The assignment consist of the keyname, a +equal sign and the value. If the value contains whitespace or the comment char, you have to +set the value between double marks. + +Options for parseInput() are: + "cc" - defines the comment char + + +XML (by Christian Stocker ) + +The main advantage of the XML-Container is, that it knows Sub-blocks. So you can +have nested blocks, which I have to use from time to time. +The XML-Container depends on the domxml implementation of PHP, therefore you +have to compile you're PHP with the option --with-domxml (or ask you're provider +to do so...) + +An example XML-file looks this way + + + + + + +The XML-File has to be well-formed, otherwise you get an error. + +The Constructor has to be called the following way + +$conf = new Config( "xml") ; + +accessing these values works the same as in the IniFile-Container, except for +Sub-blocks you can use the Directory-structure mentioned above. For example, if +you want the values of Block2: + +$conf->getValues("/block1/block2/"); + +Options (default value in brackets): + +IncludeContent [True] Since an xml-tags also can have content (not only + attributes), this flag indicates if we want to take + this as a value for the block. + example: + ... + This is content + ... + $data = $conf->getValues("/block1/"); + would give us + $data[content] = "This is content"; + + Be aware: + This is content and this also + ... + $data = $conf->getValues("/block1/"); + gives you + $data[content] = "This is content and this also"; + +IncludeChildren [True] For more flexibility in parsing your config files, + there's an array containin all the names of the sub- + blocks of one block, if you set this. + ... + + + + + ... + $data = $conf->getValues("/block1/"); + gives you an array with the values: + [key1] => value1 + [children] => Array + ( + [0] => block2 + [1] => block3 + ) + you can then get the values of block2 and block3 + with a simple foreach-loop: + + foreach ($data[children] as $childname) { + $childdata = $conf->getValues("/block1/".$childname); + } + +MasterAttribute [Null] You can assign one attribute of a block as a + MasterAttribute, then the value of this attribute + will be assigned to the parent-block with the name + of the sub-block (which have the MasterAttribute) as + the key and the value of the MasterAttribute. You + can also set "content" as the MasterAttribute. + + Example: + MasterAttribute = "key2"; + $data = $conf->getValues("/block1/"); + gives you an array with the values: + [key1] => value1 + [block2] => value2 + + +IncludeMasterAttribute [True] If you want to include the MasterAttribute also +i n the subblocks, set this flag. + MasterAttribute = "key2"; + $data = $conf->getValues("/block1/block2"); + gives you an array with the values: + [key2] => value2 + [key3] => value3 + + if you set the flag False, then [key2] will not be included. + +Limitations: + +There can't be 2 Blocks on the same level with the same name. The following will +not reasonably work: + + + + +Writing is not supported yet. you can't write back the changes you made. Since +xml files can be written in different ways meaning almost the same, it's hard to +preserve the old format. But it's certainly possible.. + +DB + +you can store you're config data also in a Database. This implementation uses +the Pear::DB Classes, so any Database supported within pear should be supported +here also. + +the table in the Database has to look the following way (the length of the +varchar does not matter and additional fields can be included.) + +CREATE TABLE config ( + datasrc varchar(50) NOT NULL, + block varchar(50) NOT NULL, + name varchar(50) NOT NULL, + value varchar(50) NOT NULL +); + +Calling the constructor: + +$conf = new Config("db","mysql://username:password@localhost/dbname") ; + +the second parameter is the dsn, as described in the pear-db documentation. + +The rest is quite the same as in the IniFile-Implementation. Just try to replace +the constructor-line in the examples for the IniFile-Container and the rest +should work. + +datasrc: it's the equivalent to the filename. so you can have different "ini-files" in the same db-table (for example to store different settings for different users) + +block/name/value: has the same meaning as in the IniFile-Container. + + + +Writing back the data is also supported. +Sub-blocks are not supported. + +Options: + +Table [config] The name of the db-table, where the keys/values/etc are + stored. + + +API-Reference + +Config($storage_driver,$storage_options = "") + + Constructor + + Parameters + storage_driver - name of the data container + storage_options - n/a + +array getBlocks( $block = "/") + + returns all blocknames of the given blockpath + + Parameters + block - existing blockpath + + Return + array - array containing all blocknames + + +mixed getValue( $block = "/", $key, $default = "") + + returns the value of the key in the block + + Parameters + block - blockpath + key - key name + value - default value, if key not found + + Return + mixed - the value of the key or the default value + + +array getValues( $block = "/") + + returns all key-value-pairs of the given blockpath + + Parameters + block - existing blockpath + + Return + array - hash containing the key-value-pairs + +mixed setValue( $block = "/", $key, $value = "") + + sets the key in the block to the given value + + Parameters + block - blockpath + key - key name + value - value to assign + + Return + mixed - the given value + +mixed parseInput ($src ,$options = array() ) + + set the data source to parse + + Parameters + src - Source to parse + ooptions - array of the settings + + Return + mixed - true on success, else PEAR_ERROR + +mixed writeInput( $dest = "", $preserve = True ) + + writes the data to dest + + Parameters + dest - Destination for writing + preserve - if true, try to preserve writing + + Return + mixed - true if success, else PEAR_ERROR + + -- 2.40.0