summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Stocker <chregu@php.net>2001-07-02 09:14:28 +0000
committerChristian Stocker <chregu@php.net>2001-07-02 09:14:28 +0000
commita65e4e91a14d933b5570673ebe8da917fd937c96 (patch)
treeeb5ef67ceb953eaf1c40ae0e635b58768a43bd06
parent30ca718935fa61bc110ca7eff14cbba8bcf4bb55 (diff)
downloadphp-git-a65e4e91a14d933b5570673ebe8da917fd937c96.tar.gz
Moved Config-Package from Experimental to main-directory, since noone complained about it ;)
-rw-r--r--pear/Config.php329
-rw-r--r--pear/Config/Container.php171
-rw-r--r--pear/Config/Container/IniFile.php219
-rw-r--r--pear/Config/Container/db.php175
-rw-r--r--pear/Config/Container/xml.php154
-rw-r--r--pear/Config/README.Config428
6 files changed, 1476 insertions, 0 deletions
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 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexander Merz <alexander.merz@t-online.de> |
+// | Christian Stocker <chregu@phant.ch> |
+// +----------------------------------------------------------------------+
+//
+// $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 <alexander.merz@t-online.de>
+* @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 @@
+<?php
+// +---------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +---------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +---------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and areunable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +---------------------------------------------------------------------+
+// | Authors: Alexander Merz <alexander.merz@t-online.de> |
+// | Christian Stocker <chregu@phant.ch> |
+// +---------------------------------------------------------------------+
+//
+// $Id$
+
+/**
+* Interface for Config-Classes
+*
+* This interface provides a API for configuration data
+*
+* @author Alexander Merz <alexander.merz@t-online.de>
+* @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 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexander Merz <alexander.merz@t-online.de> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+require_once( "Config/Container.php" ) ;
+
+/**
+* Config-API-Implemtentation for Ini-Files
+*
+* This class implements the Config-API based on ConfigDefault
+*
+* @author Alexander Merz <alexander.merz@t-online.de>
+* @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 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Christian Stocker <chregu@phant.ch> |
+// +----------------------------------------------------------------------+
+//
+// $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 <chregu@phant.ch>
+* @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__."<br>\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__."<br>\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 @@
+<?php
+//+----------------------------------------------------------------------+
+// | PHP version 4.0 |
+//+----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+//+----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and areunable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+//+----------------------------------------------------------------------+
+// | Authors: Christian Stocker <chregu@phant.ch> |
+// | Alexander Merz <alexander.merz@t-online.de> |
+//+----------------------------------------------------------------------+
+//
+
+require_once( "Config/Container.php" ) ;
+
+/**
+* Config-API-Implemtentation for XML-Files
+*
+* This class implements the Config-API based on ConfigDefault
+*
+* @author Christian Stocker <chregu@nomad.ch>
+* @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 ------
+
+<?php
+
+require_once( "config.php" ) ;
+
+$path = "c:/windows/system/" ; // change this to the location of $file
+$file = "php.ini" ; // filename to parse
+$options["cc"] = ';' ; // set the comment char
+$blocks = array() ; // contains all blocknames
+$data = $array() ; // contains all key-value-pairs
+
+$conf = new Config( "IniFile") ; // Data container is a ini file
+
+$conf -> 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
+?>
+<html>
+<head>
+ <title>Test for PEAR - Config</title>
+</head>
+<body>
+ <table>
+ <?php
+ foreach( $data as $d) {
+ echo "<tr><td colspan=\"2\">Block: $d</td></tr>" ;
+ foreach( $d as $k => $v ) {
+ echo "<tr><td>$k</td><td>$v</td></tr>" ;
+ }
+ }
+ ?>
+ </table>
+</body>
+
+----- 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 <chregu@phant.ch>)
+
+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
+
+<?xml version="1.0"?>
+ <block1 key1="value1">
+ <block2 key2="value2" key3="value3"/>
+ </block1>
+
+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:
+ ...
+ <block1>This is content</block1>
+ ...
+ $data = $conf->getValues("/block1/");
+ would give us
+ $data[content] = "This is content";
+
+ Be aware:
+ <block1>This is content<block2> and this also</block2></block1>
+ ...
+ $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.
+ ...
+ <block1 key1="value1">
+ <block2 key2="value2" key3="value3"/>
+ <block3 key4="value2" key5="value3"/>
+ </block1>
+ ...
+ $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:
+
+ <block2 key2="value2" key3="value3"/>
+ <block2 key2="value3" key4="value4"/>
+
+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
+
+