PX : code

Bs_IniHandler by fab5
Download this code


<?php
define
('BS_INIHANDLER_VERSION',      '4.0.$x$');

define('BS_INIHANDLER_UNQUOTE_NONE',   0);
define('BS_INIHANDLER_UNQUOTE_DOUBLE'1);
define('BS_INIHANDLER_UNQUOTE_SINGLE'2);
define('BS_INIHANDLER_UNQUOTE_ALL',    3);


/*************************************************************************
* This class provides methods to work with ini-style files.



*   E.g.
*      # Comment
*      [Some test data]
*         one = hallo
*         two = "hallo"
*         food = "Tom's Pizza = 'good stuff'"
*         more food = Sam's Pizza's = 'best stuff'
*         empty = ""
*         noVal =
*      [more test data]
*         one = hi
*         two = 'hi'
*         food = 'Pizza = "good"'
*         empty = ''
*         noVal

* no dependencies here.

* @author    andrej arn <andrej at blueshoes dot org>, Sam Blum <sam at blueshoes dot org>
* @copyright blueshoes.org
* @version   4.0.$id$
* @package   util
* @access    public
*/
class Bs_IniHandler extends Bs_Object {
  
  
  
/**
  * specifies which chars at the start of a line define a comment line.
  * the line is left-trimmed before the comparison is made.
  * 
  * default: '#', '/', ';'
  * 
  * note: you can only specify chars, not strings. so if you want '//' as 
  *       comment char, you need to define '/'. (which is done by default)
  * 
  * @access public
  * @var    array $commentChars (vector)
  */
  
var $commentChars = array('#''/'';');
  
  
/**
  * should quoted values be unquoted?
  * 
  *   0 = no
  *   BS_INIHANDLER_UNQUOTE_SINGLE = only single-quotes 'like this'
  *   BS_INIHANDLER_UNQUOTE_DOUBLE = only double-quotes "like this"
  *   BS_INIHANDLER_UNQUOTE_ALL    = single and double quotes (default)
  * 
  * @access public
  * @var    int $unQuote
  */
  
var $unQuote BS_INIHANDLER_UNQUOTE_ALL;
  
  
/**
  * vector with the sections as strings.
  * 
  * note: since this var is not really needed, and all information is available in 
  *       $this->_params, this internal var may disappear in the future.
  * 
  * @access private
  * @var    array $_sections
  */
  
var $_sections;
  
  
/**
  * 2-dim hash where the first dim is a hash with the section names, the 2nd 
  * is the key/value pair hash.
  * @access private
  * @var    array $_params
  */
  
var $_params;
  
  
/**
  * @todo make private
  */
  
var $comments;
  
  
/**
  * the fullpath to the currently used file.
  * @var string $_fileFullPath
  */
  
var $_fileFullPath;
  
  
/**
  * the last error message of the error that occured. not set = no error.
  * @access private
  * @var    string $_lastError
  */
  
var $_lastError;
  
  
  
/**
  * Constructor.
  * WARNING: please do not use the param $fileFullPath here, better call loadFile() yourself 
  * because otherwise you won't know if it worked or not. 
  * @param string $fileFullPath
  */
  
function Bs_IniHandler($fileFullPath='') {
    
parent::Bs_Object(__FILE__); //call parent constructor.
    
if (!empty($fileFullPath)) {
      
$this->loadFile($fileFullPath);
    }
  }
  
  
  
/**
  * Loads the given file (read in and parse).
  * @access public
  * @param  string $fileFullPath (a fullpath to the desired file.)
  * @return bool (see getLastError())
  */
  
function loadFile($fileFullPath) {
    
$this->reset();
    
    if (!
file_exists($fileFullPath)) {
      
$this->_lastError "File doesn't exists: '{$fileFullPath}'";
      return 
FALSE;
    }
    if (!
is_readable($fileFullPath)) {
      
$this->_lastError "File is not readable: '{$fileFullPath}'";
      return 
FALSE;
    }
    
    
$this->_fileFullPath $fileFullPath;
    
    
$fileContent file($fileFullPath);
    
$this->_parseFromArray($fileContent);
    
    return 
TRUE;
  }
  
  
  
/**
  * loads the ini stuff from the given string instead of a file (read in and parse).
  * @access public
  * @param  string $str
  * @return bool (see getLastError())
  */
  
function loadString($str) {
    
$this->reset();
    
    
$arr explode("\n"$str);
    
$this->_parseFromArray($arr);
    
    return 
TRUE;
  }
  
  
  
/**
  * sets the quote handling.
  * @access public
  * @param  int $mode (see constants)
  * @return void
  */
  
function setQuoteHandling($mode=BS_INIHANDLER_UNQUOTE_ALL) {
    
$this->unQuote $mode;
  }
  
  
  
/**
  * gets called from loadFile() and loadString() to parse the data.
  * @access private
  * @param  array (vector filled with strings (lines))
  * @return void
  */
  
function _parseFromArray($arr) {
    
$this->comments = array();
    
$comment = array();
    
$section '';
    
    foreach(
$arr as $line) {
      
$sectionFound $valueFound FALSE;
      
$param = array('key'=>'''val'=>'');
      do { 
// try
        
$line trim($line);
        
        
# Skip empty lines
        
if (empty($line)) break; // try
        
        # Comment
        
if (in_array($line[0], $this->commentChars)) {
          
$comment[] = $line;
          break; 
// try
        
}
        
        
# Section
        
if (preg_match('/\[(.*)\]/'$line$ar)) {
          
$section $ar[1];
          
$sectionFound TRUE;
          break; 
// try
        
}
        
        
# Parameter
        // split 1x at first '='
        
$tmp explode('='$line);
        if (!
is_array($tmp)) break; // try 
        
if (sizeOf($tmp) < 2) {
          
//invalid comment line, whatever.
          //no good if we arrive here. that's some crappy line that should not be in the file.
          //we could issue a warning here.
          
$comment[] = @$tmp[0];
          break;
        }
        
        
$param['key'] = trim($tmp[0]);
        
array_shift($tmp);
        if (
sizeOf($tmp)>1$tmp[0] = implode('='$tmp);
        
$param['val'] = isSet($tmp[0]) ? trim($tmp[0]) : '';
        if (empty(
$param['val'])) {
          
$valueFound TRUE;
          break; 
// try
        
}
        
        
        
$unQuote '';
        if (
$this->unQuote BS_INIHANDLER_UNQUOTE_DOUBLE$unQuote .= '"';
        if (
$this->unQuote BS_INIHANDLER_UNQUOTE_SINGLE$unQuote .= "'";
        if (empty(
$unQuote)) {
          
$valueFound TRUE;
          break; 
// try
        
}
        
        
// trim quote
        
$regEx '/^(['.$unQuote.']?)(.*)\1$/';
        if (
preg_match($regEx$param['val'], $ar)) {
          
$param['val'] = $ar[2];
          
$valueFound TRUE;
          break; 
// try
        
} else {
          
//the value had unmatching quotes, like "here' or 'here"
          
break; // try
        
}
      } while(
FALSE);
      
      if (
$sectionFound) {
        
$this->_sections[] = $section;
        if (!empty(
$comment)) $this->comments[$section] = $comment;
        
$comment = array();
      } else if (
$valueFound) {
        
$this->_params[$section][$param['key']] = $param['val'];
        if (!empty(
$comment)) $this->comments[$section .'__'$param['key']] = $comment;
        
$comment = array();
      }
    } 
// foreach
    
if (!empty($comment)) $this->comments['__LastComment__'] = $comment;
  }
  
  
  
/**
  * 
  */
  
function toString() {
    
$outStr "# Bs_IniHandler File generated by www.blueshoes.org\n\n";
    foreach (
$this->_params as $section => $params) {
      if (isSet(
$this->comments[$section])) {
        foreach (
$this->comments[$section] as $comment$outStr .= "{$comment}\n";
      }
      
$outStr .= "[".$section."]\n"
      foreach (
$params as $key => $value) {
        if (isSet(
$this->comments[$section .'__'$key])) {
          foreach (
$this->comments[$section .'__'$key] as $comment$outStr .= "  {$comment}\n";
        }
        
$outStr .= "  " .$key" = " .$value"\n"
      }
      
$outStr .= "\n";
    }
    
    if (isSet(
$this->comments['__LastComment__'])) {
      foreach (
$this->comments['__LastComment__'] as $comment$outStr .= "{$comment}\n";
    }
    return 
$outStr;
  }
  
  
  
/**
  * saves the ini settings to the file specified.
  * @access public
  * @param  string $fileFullPath (see above)
  * @return bool (see getLastError())
  * @see    saveString()
  */
  
function saveFile($fileFullPath) {
    
$outStr $this->toString();
    if (!
$fp fopen($this->_fileFullPath'wb')) {
      
$this->_lastError "Failed open the file for writing: '{$fileFullPath}'";
      return 
FALSE;
    }
    if (!
fwrite($fp$outStr)){
      
$this->_lastError "Failed to write (but was able to open) the file: '{$fileFullPath}'";
      return 
FALSE;
    }
    
    @
fclose($fp);
    return 
TRUE;
  }
  
  
/**
  * resets this object so we can re-use it for something else.
  * some setting vars are not reset.
  * 
  * resets:
  *   _sections
  *   _params
  *   _fileFullPath
  *   _lastError
  *   
  * keeps:
  *   commentChars
  *   unQuote
  * 
  * @access public
  * @return void
  */
  
function reset() {
    unset(
$this->_sections);
    unset(
$this->_params);
    unset(
$this->_fileFullPath);
    unset(
$this->_lastError);
  }
  
  
 
/**
  * returns [all parameters|parameter] [for the given section].
  * 
  * examples:
  *   get()                 => returns all sections with all params as 2-D hash.
  *                              array of [<section>][<key>]  => <string>
  *   get('section')        => returns all params for the section specified  as 1-D hash.
  *                              array of [<key>]  => <string>
  *   get('section', 'key') => returns the param specified of the section specified as string.
  * 
  * note: if a param is defined in the 'global scope', use an empty string for the 
  *       $section name. example: get('', 'key')
  * 
  * @access public
  * @param  string $section if not given returns all sections
  * @param  string $key if not given returns all keys
  * @return mixed (see above)
  * @throws null  (if the given section or key does not exist)
  */
  
function get($section=NULL$key=NULL) {
    if (
is_null($section)) return $this->_params;
    if (!isSet(
$this->_params[$section])) return NULL//throw
    
if (is_null($key))     return $this->_params[$section];
    if (!isSet(
$this->_params[$section][$key])) return NULL//throw
    
return $this->_params[$section][$key];
  }
  
  
  
/**
  * tells if the section or key specified is set.
  * 
  * examples:
  *   has('mySection')         => tells if 'mySection' is set
  *   has('mySection', 'myKey' => tells if myKey in mySection is set.
  * 
  * note: case matters!
  * 
  * @access public
  * @param  string $section
  * @param  string $key (default is NULL)
  * @return bool
  */
  
function has($section$key=NULL) {
    if (
is_null($key)) {
      return (isSet(
$this->_params[$section])); //using _params instead of _sections cause it's a hash. in_array is slower.
    
} else {
      return (isSet(
$this->_params[$section]) && isSet($this->_params[$section][$key]));
    }
  }
  
  
  
/**
  * returns the text message of the last error that occured.
  * 
  * call this function if something failed, for example after getting 
  * bool FALSE back from loadFile().
  * 
  * @access public
  * @return mixed (string last error, or NULL if no error occured.)
  */
  
function getLastError() {
    if (
is_null($this->_lastError)) return NULL;
    return 
$this->_lastError;
  }
}

#####################################################
#  SELF - Test
#####################################################
if (basename($_SERVER['PHP_SELF']) == 'Bs_IniHandler.class.php') {
    
  
  
$testData =<<<EOD
   # Comment 1
   # comment 2
   []
   globalData = foo
   
   # comment A
   [Some test data]
      # comment B
      one = hallo
      two = "hallo"
      # comment C
      food = "Tom's Pizza = 'good stuff'"
      more food = Sam's Pizza's = 'best stuff'
      empty = ""
      noVal =
   # comment D
   [more test data]
      one = hi
      two = 'hi'
      food = 'Pizza = "good"'
      empty = ''
      noVal
   # comment E
EOD;
  
  
$iniHandler = new Bs_IniHandler();
  
$iniHandler->loadString($testData);
  
  
#XR_dump($iniHandler->comments, __LINE__, '', __FILE__);
  
XR_dump($iniHandler->toString(), __LINE__''__FILE__);
}

?>

Comments or questions?
PX is running PHP 5.2.17
Thanks to Miranda Productions for hosting and bandwidth.
Use of any code from PX is at your own risk.