Location: PHPKode > projects > toKernel > tokernel.1.0.6/tokernel.framework/lib/ini.lib.php
<?php
/**
 * toKernel - Universal PHP Framework.
 * ini file processing library
 * 
 * This file is part of toKernel.
 *
 * toKernel is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * toKernel is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with toKernel. If not, see <http://www.gnu.org/licenses/>.
 *
 * @category   framework
 * @package    toKernel
 * @subpackage library
 * @author     toKernel development team <hide@address.com>
 * @copyright  Copyright (c) 2011 toKernel
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @version    1.1.3
 * @link       http://www.tokernel.com
 * @since      File available since Release 1.0.0
 * @todo	   Create functions - sync, compare.
 */

/* Restrict direct access to this file */
defined('TK_EXEC') or die('Restricted area.');

/**
 * ini_lib class
 * 
 * @author David Ayvazyan <hide@address.com>
 */
class ini_lib {
	
/**
 * Library object for working with 
 * libraries in this class
 * 
 * @var object
 * @access protected
 */ 
 protected $lib;

/**
 * Main ini array, content of ini file.
 * 
 * @access protected
 * @var array
 */ 
 protected $ini_arr;

/**
 * Loaded fiel name
 * 
 * @access protected
 * @var string
 */ 
 protected $file;
 
/**
 * String -end of file.
 * Ex: "; End of file. Last update 05-03-2010" 
 * 
 * @access protected
 * @var string
 */ 
 protected $end_of_file;
 
/**
 * Is file opened as new (empty).
 * 
 * @access protected
 * @var bool
 */ 
 protected $is_new_file;
 
/**
 * Comment start char.
 * 
 * @access protected
 * @var string
 */ 
 protected $comment_sign;
 
/**
 * Is file opened as readonly
 * This value will be true, if loading 
 * ini file with specified section.
 * 
 * @access protected
 * @var bool
 */ 
 protected $is_file_read_only;

/**
 * Is loaded file have sections
 * 
 * @access protected
 * @var bool
 */
 protected $have_sections;

/**
 * Class constructor
 * 
 * @access public
 * @return void
 */ 
 public function __construct() {
	
	$this->lib = lib::instance();
	
	$this->ini_arr = array();
	$this->file = '';
	$this->is_new_file = false;
	$this->end_of_file = '';
	$this->comment_sign = ';';
	$this->is_file_read_only = false;
	$this->have_sections = false;
	$this->end_of_file = $this->comment_sign . 
								' End of file. Last update: ' . date('d-m-Y');
 } // end constructor

/**
 * Class destructor
 * 
 * @access public
 * @return void
 */ 
 public function __destruct() {
	unset($this->ini_arr);
	unset($this->file);
	unset($this->is_new_file);
	unset($this->end_of_file);
	unset($this->comment_sign);
	unset($this->is_file_read_only);
	unset($this->have_sections);	
 } // end destructor

/**
 * Magic function. Return item from ini.
 * 
 * @access public
 * @param string $item
 * @return mixed
 */ 
 public function __get($item) {
	return $this->item_get($item);
 }

/**
 * Magic function set. set ini item value.
 * 
 * @access public
 * @param string $item
 * @param mixed $value
 * @return bool
 */ 
 public function __set($item, $value) {
	return $this->item_set($item, $value);
 }

 /**
  * Magic function isset. return, is item exists.
  * 
  * @access public
  * @param string $item
  * @return mixed
  */
 public function __isset($item) {
	return $this->item_exists($item);
 }

/**
 * Magic function unset. delete ini item.
 * 
 * @access public
 * @param string $item
 * @return mixed
 */ 
 public function __unset($item) {
	$this->item_delete($item);
 }

/**
 * Load ini file and return cloned instance of this object. 
 * 
 * @access public
 * @param string $file
 * @param string $section
 * @param bool $allow_cnif
 * @return mixed object | bool
 */
 public function instance($file, $section = NULL, $allow_cnif = false) {
	
	if(!is_readable($file) and $allow_cnif == false) {
		return false;
	}
	
	$this->__construct();
	
	if(!$this->file_load($file, $section, $allow_cnif)) {
		return false;
	}
	
	return clone $this;
 } // end func instance

/**
 * Load ini file
 * 
 * @access public
 * @param string $file
 * @param string $section
 * @param bool $allow_cnif
 * @return bool
 */ 
 public function file_load($file, $section = NULL, $allow_cnif = false) {
	
	if(!is_readable($file) and $allow_cnif == false) {
		return false;
	}
	
	if(!is_readable($file)) {
		$this->file = $file;
		$this->is_new_file = true;
		return true;
	}
	
	$lines = file($file, FILE_IGNORE_NEW_LINES);
	
	$this->file = $file;
	
	$current_section = NULL;
	
	foreach($lines as $line) {
	
	    $line = trim($line);
		
	    // Define section
	    if(substr($line, 0, 1) == '[' and substr($line, -1) == ']') {
	    	
	        // Get section name
	    	$tmp_section = trim(substr($line, 1, -1));
			
	    	if($tmp_section != '') {
	    	
	    		$current_section = $tmp_section; 
	    	
				$this->section_set($current_section);
				$this->have_sections = true;
			} else {
				$this->comment_set('; INVALID LINE: ['.$tmp_section.']');
			}
			
		// define comment	
	    } elseif($this->is_comment($line) == true) {
			
			if($this->is_end_of_file($line) == false) {
				$this->comment_set($line, $current_section);
			} 
			
		// define params
	    } else {
		  	if($line != '') {
		  		$equal_sign_pos = strpos($line, '=');
		  	
		  		if($equal_sign_pos === false) {
		  			$line = '; INVALID LINE: ' . $line;
		  			
		  			$this->comment_set($line, $current_section);
		  		} else {
		    		$key = trim(substr($line, 0, $equal_sign_pos));
		    		$value = trim(substr($line, $equal_sign_pos+1));
		    		
		    		$this->item_set($key, $value, $current_section);
		  		}
		  	} else {
		  		$this->comment_set($line, $current_section);
		  	}	
	    } 
		
	} // End foreach lines
	
	unset($lines);
	
	if(is_null($section)) {
		return true; 	
	}
		
	if(!$this->section_exists($section)) {
		return false;
	}	

	$tmp_arr = $this->ini_arr[$section];
	$this->ini_arr = array();
	$this->ini_arr[$section] = $tmp_arr;
	$this->is_file_read_only = true;
	
	return true;
	
 } // end func file_load

/**
 * Save ini file
 * 
 * @access public 
 * @param string $file
 * @param bool $overwrite
 * @return bool
 */
 public function file_save($file = NULL, $overwrite = false) {
	
	if($this->is_file_read_only == true and $overwrite == false 
													and is_null($file)) {
		return false;
	}

	if(is_file($file) and $overwrite == false) {
		return false;
	}

	if(is_null($file)) {
		$file = $this->file;
	}
	
	if($file == '') {
		return false;
	}
	
	$ini_buf = '';
	
	foreach($this->ini_arr as $key => $value) {
		
		if(!is_array($value)) {
			
			if(is_numeric($key)) {
				$ini_buf .= $value . "\n";
			} else {
				$ini_buf .= $key . '=' . $value . "\n";
			}

		} else {
			$ini_buf .= '[' . $key . ']' . "\n";
			
			foreach($value as $s_key => $s_value) {

				if(is_numeric($s_key)) {
					$ini_buf .= $s_value . "\n";
				} else {
					$ini_buf .= $s_key . '=' . $s_value . "\n";
				}
	
			} // End foreach section items
			
			//$ini_buf .= "\n";
		}

	} // end foreach
	
	$ini_buf = trim($ini_buf);
	$ini_buf .= "\n" . $this->end_of_file;
	
	if(!is_writable($file)) {
		trigger_error('File `'.$file.'` is not writable!', E_USER_WARNING);
		return false;
	} else {
		return file_put_contents($file, $ini_buf);
	}	
	
 } // end func file_save

/**
 * Destroy object values.
 * 
 * @access public
 * @return void
 */
 public function file_unload() {
	$this->__construct();
 } // end func file_unload

/**
 * Delete loaded ini file
 * 
 * @access public
 * @return bool
 */
 public function file_delete() {
	
	if(is_writable($this->file) == false) {
		return false;
	}
	
	$tmp_file = $this->file;
	$this->__construct();
	
	return unlink($tmp_file);
 } // end func file_delete

/**
 * Return loaded ini file name
 * 
 * @access public
 * @return string
 */
 public function file_name() {
	return $this->file;
 }

/**
 * Create section if not exists.
 * Items array is optional.
 *
 * @access public
 * @param string $section
 * @param array $items
 * @return bool
 */
 public function section_set($section, $items = array()) {

	$section = trim($section);

	if($this->section_exists($section) or !$this->is_valid($section)) {
		return false;
	}
	
	$this->ini_arr[$section] = array();
		
	if(count($items) > 0) {
		foreach($items as $item => $value) {
			if($this->is_valid($item)) {
				$this->item_set($item, $value, $section);
			}
		}
	}

	return true;
	
 } // end function section_set

/**
 * Return array of existing sections
 *
 * @access public
 * @return array
 */
 public function sections() {
	
 	$sections = array();

 	foreach($this->ini_arr as $section => $items) {
		if(!is_numeric($section)) {
			$sections[] = $section;
		}
	}
 	return $sections;
 	
 } // end function sections_list 
 
/**
 * Return section as array.
 * 
 * @access public
 * @param string $section
 * @return mixed array | bool
 */
 public function section_get($section) {
	
	if(isset($this->ini_arr[$section]) and is_array($this->ini_arr[$section])) {
		return $this->array_clean($this->ini_arr[$section]);
	} else {
		return false;
	} 
	
 } // end func section_get

/**
 * Check is section exists
 * 
 * @access public
 * @param string $section
 * @return bool
 */ 
 public function section_exists($section) {
	if(isset($this->ini_arr[$section]) and is_array($this->ini_arr[$section])) {
		return true;
	} else {
		return false;
	}
 } // end func section_exists

/**
 * Rename section name
 * 
 * @access public
 * @param string $section
 * @param string $new_section
 * @return bool
 */ 
 public function section_rename($section, $new_section = NULL) {
	
	$new_section = trim($new_section);
	$section = trim($section);
		
	if($this->section_exists($section) == false or 
							$this->is_valid($section) == false or 
							$this->is_valid($new_section) == false) {
		return false;
	}

	$this->ini_arr = $this->lib->data->array_key_rename($section, 
												$new_section, $this->ini_arr);
	
	return true;
		
} // end func section_rename

/**
 * Delete section
 * 
 * @access public
 * @param string $section
 * @return bool
 */
 public function section_delete($section) {
	if($this->section_exists($section)) {
		unset($this->ini_arr[$section]);
		return true;
	} else {
		return false;
	}
 } // end func section_delete

/**
 * Sort section item.
 * Foe now, this function will remoe all comments in sorted section.
 * If the argument $section_or_entire_ini_arr defined as true, then
 * function will sort entire ini file, else if section name specified, 
 * function will sort specified section.
 * 
 * @access public
 * @param mixed string | bool $section_or_entire_ini_arr
 * @return bool
 */
 public function section_items_sort($section_or_entire_ini_arr) {
	
	// case, when section name set
	if(is_string($section_or_entire_ini_arr)) {
		if($this->section_exists($section_or_entire_ini_arr) == true) {
			
			$this->ini_arr[$section_or_entire_ini_arr] = 
				$this->array_clean($this->ini_arr[$section_or_entire_ini_arr]);
				
			ksort($this->ini_arr[$section_or_entire_ini_arr]);
			return true;
		} else {
			return false;
		}
	} elseif($section_or_entire_ini_arr === true) {
		foreach($this->ini_arr as $key => $value) {
			if(is_array($value) and !is_numeric($key)) {
				$this->ini_arr[$key] = $this->array_clean($this->ini_arr[$key]);
				ksort($this->ini_arr[$key]);
			}
		}
		return true;
	} 
	
	return false;
 } // end func section_items_sort

/**
 * Set item value.
 * If the value defined as bool, then it will converted to 1 or 0.
 * 
 * @access public
 * @param string $item
 * @param mixed $value
 * @param string $section
 * @return bool
 */
 public function item_set($item, $value, $section = NULL) {
	
	if(!$this->is_valid($item)) {
		return false;
	}

	if(is_bool($value) and $value === true) {
		$value = '1';
	} elseif(is_bool($value) and $value === false) {
		$value = '0';
	}
	
	if(!is_null($section)) {
		if(!$this->is_valid($section)) {
			return false;
		}

		$this->ini_arr[$section][$item] = $value;
	} else {
		
		// Set this item on top, before any section start.
		if(!isset($this->ini_arr[$item]) and $this->have_sections == true) {
			$this->ini_arr = array($item => $value) + $this->ini_arr;
		} else {	 
			$this->ini_arr[$item] = $value;
		}	
	}
	
	return true;
	
 } // end func item_set

/**
 * Return item value
 * if second argument defined as true, then this function
 * will return first finded item in entire ini array
 * 
 * @access public
 * @param string $item
 * @param mixed string | bool $section_or_forcibly
 * @return mixed
 */ 
 public function item_get($item, $section_or_forcibly = NULL) {

	$item = trim($item);

	if($this->is_valid($item) == false) {
		return false;
	}
	
	// Find and return first item value of entire ini array.
	if($section_or_forcibly === true) {
		// search in main ini entire array
		
		if($this->item_exists($item) === true) {
			// exists in array root sections
			return $this->ini_arr[$item];
		} else {
			// search in sections
			foreach($this->ini_arr as $key => $value) {
				if(is_array($value) and is_string($this->item_exists($item, $key))) {
					return $value[$item];
				}
			}
		}
		
		return false;
	}

	// Find and return by item name only 
	if(is_null($section_or_forcibly) and $this->item_exists($item) === true) {
		return $this->ini_arr[$item];
	}
	
	if($this->is_valid($section_or_forcibly)) {
		
		$in_section = $this->item_exists($item, $section_or_forcibly);
		
		if(is_string($in_section)) {
			return $this->ini_arr[$section_or_forcibly][$item];
		}
	}
	
	return false;
 } // end func item_get

/**
 * Check is item exists
 * if item found in section, then return section name, else
 * return true if item found behind a section.
 * 
 * @access public
 * @param string $item
 * @param string $section
 * @return mixed string | bool 
 */
 public function item_exists($item, $section = NULL) {
	
	if(!$this->is_valid($item)) {
		return false;
	}
	// Find in entire array
	if(is_null($section) and isset($this->ini_arr[$item]) 
						 and !is_array($this->ini_arr[$item])) {
		return true;
	}
	
	// check section 
	if(!is_null($section) and $this->is_valid($section) == false) {
		return false;
	}
	
	if(isset($this->ini_arr[$section][$item])) {
		return $section;
	}

	return false;
} // end func item_exists

/**
 * Rename item.
 * 
 * @access public
 * @param string $item
 * @param string $new_item
 * @param string $section
 * @return mixed string | bool
 */
 public function item_rename($item, $new_item, $section = NULL) {
	
	// find replace in entire array
	if(is_null($section) and $this->item_exists($item)) {
		$this->ini_arr = $this->lib->data->array_key_rename($item, $new_item, 
															$this->ini_arr);	
		return true;
	}
	
	// check section
	if(!is_null($section) and $this->is_valid($section) == false) {
		return false;
	}
	
	// Search in section if isset
	if($this->item_exists($item, $section)) {
		$this->ini_arr[$section] = $this->lib->data->array_key_rename($item, 
										$new_item, $this->ini_arr[$section]);
		return $section;
	}
	
	return false;
	
 } // end func item_rename

/**
 * Delete item
 * 
 * @access public 
 * @param string $item
 * @param string $section
 * @return mixed string | bool
 */
 public function item_delete($item, $section = NULL) {
	
	// find replace in entire array
	if(is_null($section) and $this->item_exists($item)) {
		unset($this->ini_arr[$item]);
		return true;
	}
	
	// check section
	if(!is_null($section) and $this->is_valid($section) == false) {
		return false;
	}
	
	// Search in section if isset
	if($this->item_exists($item, $section)) {
		unset($this->ini_arr[$section][$item]);
		return $section;
	}
	
	return false;
	
 } // end func item_delete

/**
 * Add comment
 * 
 * @access public
 * @param string $line
 * @param string $section
 * @return void
 */
 public function comment_set($line, $section = NULL) {
	
	if(!is_null($section)) {
		$this->ini_arr[$section][] = $line;
	} else {
		$this->ini_arr[] = $line;
	}
	
 } // end func comment_set

/**
 * Check, is data comment.
 * 
 * @access public
 * @param mixed $line
 * @return bool
 */ 
 public function is_comment($line) {
 	if(substr(trim($line), 0, 1) == $this->comment_sign) {
		return true;
	} else {
		return false;
	} 
 } // end func is_comment
 
/**
 * Check is valid section or item name.
 * Will be string.
 * 
 * @access public
 * @param mixed $data
 * @return bool
 */
 public function is_valid($data) {
	if(trim($data) == '' or is_numeric($data) or 
								$this->is_comment($data) or is_bool($data)) {
		return false;
	} else {
		return true;
	}
 } // end func is_valid

/**
 * Check, is string end of file.  
 * 
 * @access public
 * @param mixed $line
 * @return bool
 */ 
 public function is_end_of_file($line) {
	if(substr(trim($line), 0, 13) == $this->comment_sign.' End of file') {
		return true;
	} else {
		return false;
	}
 } // end func is_end_of_file

/**
 * clean array, remove comments.
 * 
 * @access protected
 * @param array $arr
 * @return mixed array | bool
 */
 protected function array_clean($arr) {
	if(!is_array($arr)) {
		return false;
	}
	
	foreach($arr as $key => $value) {
		if(is_int($key)) {
			unset($arr[$key]);
		}
	} // end foreach
	
	return $arr;
 } // end func array_clean

/* End of class ini_lib */
}

/* End of file */
?>
Return current item: toKernel