function test_string_to_array()
 {
     $str = 'A,B,indC:C,(D,E),indF:F';
     $parse = sonots::string_to_array($str);
     $truth = array('A', 'B', 'indC' => 'C', array('D', 'E'), 'indF' => 'F');
     $this->assertEqual($parse, $truth);
     $str = '';
     $parse = sonots::string_to_array($str);
     $truth = array();
     $this->assertEqual($parse, $truth);
 }
 /**
  * Restore a string to an array. 
  *
  * PHP API Extension
  * 
  * Example
  * <code>
  * $string = 'A,B,indC:C,(0:D,1:E),indF:F'
  * $array = string_to_array($string)
  * <code>
  * Output:
  * <code>
  * array('A', 'B', 'indC' => 'C', array('D', 'E'), 'indF'=>'F');
  * </code>
  *
  * @access public
  * @static
  * @param string $string
  * @param string $hashsep A character to be used as a hash key and val seperator
  * @param string $elemsep A character to be used as a elememt separator
  * @param string $openarray A character to be used as an open bracket of an array
  * @param string $closearray A character to be used as a close bracket of an array
  * @param boolean $decode Performe decode key/val
  * @return array
  * @see array_to_string
  * @version $Id: v 1.3 2008-07-15 11:14:46 sonots $
  */
 function string_to_array($string, $hashsep = ':', $elemsep = ',', $openarray = '(', $closearray = ')', $decode = true)
 {
     $result = array();
     $delims = $hashsep . $elemsep . $openarray . $closearray;
     if ($string === '') {
         return $result;
     }
     /// parse the first element
     $hashsep_pos = strpos($string, $hashsep);
     $elemsep_pos = strpos($string, $elemsep);
     $openarray_pos = strpos($string, $openarray);
     // there is a key or not for the 1st element
     if ($hashsep_pos !== false && ($elemsep_pos === false || $hashsep_pos < $elemsep_pos) && ($openarray_pos === false || $hashsep_pos < $openarray_pos)) {
         $key = substr($string, 0, $hashsep_pos);
         $key = $decode ? sonots::urldecode($key, $delims) : $key;
         $string = trim(substr($string, $hashsep_pos + 1));
     } else {
         $key = null;
     }
     $openarray_pos = strpos($string, $openarray);
     if ($openarray_pos === false || $openarray_pos > 0) {
         // hash val is not an array
         $elemsep_pos = strpos($string, $elemsep);
         if ($elemsep_pos === false) {
             $val = $decode ? sonots::urldecode($string, $delims) : $string;
             $string = "";
         } else {
             $val = substr($string, 0, $elemsep_pos);
             $val = $decode ? sonots::urldecode($val, $delims) : $val;
             $string = substr($string, $elemsep_pos + 1);
         }
     } elseif ($openarray_pos == 0) {
         // hash val is an array
         $string = substr($string, 1);
         $num_openarray = 1;
         // search where is a corresponding closet
         $string_char_array = str_split($string);
         for ($index = 0; count($string_char_array); $index++) {
             if ($string_char_array[$index] == $openarray) {
                 $num_openarray++;
             } else {
                 if ($string_char_array[$index] == $closearray) {
                     $num_openarray--;
                 }
             }
             if ($num_openarray == 0) {
                 break;
             }
         }
         $val = sonots::string_to_array(substr($string, 0, $index), $hashsep, $elemsep, $openarray, $closearray, $decode);
         $string = substr($string, $index + 2);
     }
     if (is_null($key)) {
         $result[] = $val;
     } else {
         $result[$key] = $val;
     }
     /// next element
     if (strlen($string) != 0) {
         $result = array_merge($result, sonots::string_to_array($string, $hashsep, $elemsep, $openarray, $closearray, $decode));
     }
     return $result;
 }
 /**
  * Parse option line as followings:
  *
  * Rule)
  * <code>
  * , is used to separate options.
  * = is used to separate option key (name) and option value.
  * () is used if element is an array.
  * </code>
  *
  * Example)
  * <code>
  *  $line = 'prefix=Hoge/,num=1:5,contents=(num=1,depth=1),hoge';
  *  $options = PluginSonotsOption::parse_option_line($line);
  *  var_export(); 
  *  // array('prefix'=>'Hoge/','num'=>'1:5',
  *  // 'contents'=>array('num'=>'1','depth'=>'1'),'hoge'=>true);
  *  // () becomes an array()
  *  // Option which does not have "=[value]" is set to TRUE anyway. 
  * </code>
  *
  * parse_option_line is upper version of the simple 
  * sonots::parse_options($args).  parse_options does not support 
  * array arguments, but parse_option_line does. 
  * Except array arguments, both should be able to generate 
  * the same results. 
  *
  * FYI) Decoding is required especially when key/val values include
  * delimiter characters, '=', ',', '(', and ')'. Usually use true. 
  *
  * @access public
  * @static
  * @param string $line
  * @param boolean $trim trim option key/val
  * @param boolean $decode perform decode key/val
  * @return array array of options
  * @uses sonots::string_to_array
  * @uses numeric_to_boolean
  * @see glue_option_line
  * @see sonots::parse_options
  * @version $Id: v 1.5 2008-06-07 11:14:46 sonots $
  * @since   v 1.0
  */
 function parse_option_line($line, $trim = false, $decode = true)
 {
     $array = sonots::string_to_array($line, '=', ',', '(', ')', $decode);
     $options = PluginSonotsOption::numeric_to_boolean($array);
     if ($trim) {
         $options = sonots::trim_array($options, true, true);
     }
     return $options;
 }