<?php
/* vim: set ai tabstop=4: */
// $Date: 2002/04/09 01:29:38 $
// $Revision: 1.3 $
// +----------------------------------------------------------------------+
// | CONFIG MANAGER 0.1.2 - 09-Apr-2002 |
// +----------------------------------------------------------------------+
// | Author: Keyvan Minoukadeh - keyvan@k1m.com - http://www.k1m.com |
// +----------------------------------------------------------------------+
// | PHP class for managing plain text config files. |
// +----------------------------------------------------------------------+
// | This program 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 2 |
// | of the License, or (at your option) any later version. |
// | |
// | This program 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. |
// +----------------------------------------------------------------------+
if (defined('CONFIGMAN_DIR')) {
require_once(CONFIGMAN_DIR.'class.config_base.php');
}
/**
* Config manager reader class
*
* Use this class to read plain text config files.
* This is an extension to the base config class.
*
* @author Keyvan Minoukadeh <keyvan@k1m.com>
* @version 0.1.2
*/
class config_reader extends config_base
{
/**
* fetch mode
*
* CONFIGMAN_FETCH_ASSOC = return config vars as associative array
* CONFIGMAN_FETCH_OBJECT = return config vars as an object
*/
var $fetch_mode = CONFIGMAN_FETCH_ASSOC;
/**
* allow serialize?
*/
var $cache_config = false;
/////////////
// methods
/////////////
/**
* Constructor
*
* @param string $config config file to use
* @param int $fetch_mode fetch mode
*/
function config_reader($config=null, $fetch_mode=null)
{
// run base constructor
if (!is_null($config)) {
$this->config_base($config);
}
if (!is_null($fetch_mode)) {
$this->fetch_mode = $fetch_mode;
}
}
/**
* Load config
*
* Parse and update class variables with values found in config.
* Cache file refers to a file containing the serialized values found in the config file,
* if present it will load the values quicker.
*
* @param int $fetch_mode either CONFIGMAN_FETCH_ASSOC or CONFIGMAN_FETCH_OBJECT
* @param bool $reload if true, will reload values from main config even if cache exists
* @return mixed either associative array, or object
*/
function load($fetch_mode=null, $reload=false)
{
$func_name = 'load';
clearstatcache();
if (!$this->is_file_valid()) {
if ($this->debug) $this->_debug("$func_name: Invalid config file");
}
$config = $this->config;
$file_cache = $this->config_cache_dir.basename($config).'.cache';
// grab file modification times
$config_ts = @filemtime($config);
$file_cache_ts = @filemtime($file_cache);
// check if cache file available
if (!$reload && file_exists($file_cache) && is_readable($file_cache) && ($config_ts == $file_cache_ts)) {
if ($this->debug) $this->_debug("$func_name: Loading from cache file");
// unserialize config cache (returns associative array)
$fp = fopen($file_cache, 'r');
// shared lock
flock($fp, 1);
$contents = fread($fp, filesize($file_cache));
// release lock
flock($fp, 3);
fclose($fp);
$config_array = unserialize($contents);
if (!is_array($config_array)) {
if ($this->debug) $this->_debug("$func_name: Invalid config cache ($file_cache), remove and try again");
return false;
}
// loop through associative array and update vars
foreach ($config_array as $key => $val) {
$this->set_param_from_array($key, $val);
}
} elseif (file_exists($config) && is_readable($config)) {
if ($this->debug) $this->_debug("$func_name: Loading from config file");
// parse config file
$this->_process_config(file($config));
if ($this->cache_config) {
$this->_write_config_cache($file_cache, filemtime($config));
}
} else {
if ($this->debug) $this->_debug("$func_name: Could not load config file ($config)");
return false;
}
return $this->_return_config_vars($fetch_mode);
}
/**
* Load config from string
*
* Same as load() except first parameter will be the string contaning
* the config lines. (useful if you're not storing the config in a file, eg. using a DB
* or generating it some way)
*
* @param string $config config contents as string
* @param int $fetch_mode either CONFIGMAN_FETCH_ASSOC or CONFIGMAN_FETCH_OBJECT
* @return mixed either associative array, or object
*/
function load_from_string($config, $fetch_mode=null)
{
$func_name = 'load_from_string';
$config = explode("\n", trim($config));
if (is_array($config) && (count($config) > 0)) {
$this->_process_config($config);
return $this->_return_config_vars($fetch_mode);
} else {
if ($this->debug) $this->_debug("$func_name: Invalid config contents");
return false;
}
}
///////////////////////
// PRIVATE FUNCTIONS //
///////////////////////
/**
* Return config vars
*
* @param int $fetch_mode CONFIGMAN_FETCH_ASSOC or CONFIGMAN_FETCH_OBJECT or null for default
* @return mixed array or object, depending on fetch mode
* @access private
*/
function _return_config_vars($fetch_mode=null)
{
$return = array();
foreach ($this->param as $key => $val) {
$return["$key"] = $val[(string)key($val)]['value'];
}
if (is_null($fetch_mode)) {
$fetch_mode = $this->fetch_mode;
}
if ($fetch_mode === CONFIGMAN_FETCH_OBJECT) {
return (object)$return;
} else {
return $return;
}
}
/**
* Write config cache
*
* @param string $file_path serialize config array and write to disk
* @param int $timestamp unix timestamp to set as modification time
* @return bool
* @access private
* @see load
*/
function _write_config_cache($file_path, $timestamp=null)
{
$func_name = '_write_config_cache';
if ($this->cache_config) {
$serialized = serialize($this->param);
$fp = @fopen($file_path, "w");
if (!$fp) {
if ($this->debug) $this->_debug("$func_name: Could not create cache file");
return false;
} else {
// exclusive lock
flock($fp, 2);
$result = @fwrite($fp, $serialized);
// release lock
flock($fp, 3);
fclose($fp);
if (!$result) {
if ($this->debug) $this->_debug("$func_name: Could not write cache file");
return false;
} else {
if ($this->debug) $this->_debug("$func_name: Cache file written");
// touch file with modification time of main config file (used for comparison)
touch($file_path, ((is_null($timestamp) || !$timestamp) ? time() : $timestamp));
@chmod($file_path, 0600);
return true;
}
}
}
return false;
}
/**
* Process config
*
* @param array $config each line of the config file as a seperate array element
* @return bool true if successfully loaded, false otherwise
* @access private
*/
function _process_config($config)
{
$func_name = "_process_config";
if (!is_array($config) || (count($config) < 1)) {
if ($this->debug) $this->_debug("$func_name: Config file is empty, or is not an array");
return false;
}
$var = array();
$section = $this->default_section;
$comment = "";
$line_count = 0;
foreach ($config as $line) {
$line_count++;
$line = trim($line);
if (empty($line)) {
continue;
}
// plain comment
if (preg_match('!^'.preg_quote($this->comment).'(.*)$!', $line, $match)) {
if (trim($match[1]) != '') {
$comment .= trim($match[1])."\n";
}
unset($match);
continue;
}
// variable definition
if (preg_match('!^'.$this->regex_type.'?('.$this->regex_var.')(\.'.$this->regex_assoc.')?\s*'.preg_quote($this->separator).'\s*(?'.'>(["\'])?)(.*)(?(4)\4)$!i', $line, $match)) {
if (trim($match[1]) != '') {
$type = $this->type_prefix[$match[1]];
} elseif ($this->default_type != 'auto') {
$type = $this->default_type;
} elseif ($match[4] == '"' || $match[4] == '\'') {
$type = CONFIGMAN_TYPE_STRING;
} else {
$type = CONFIGMAN_TYPE_INTEGER;
}
// replace escaped characters (\n \r and \t)
if ($match[4] == '"') {
$match[5] = preg_replace(array('/(?<!\\\\)\\\\n/','/(?<!\\\\)\\\\r/','/(?<!\\\\)\\\\t/'), array("\n","\r","\t"), $match[5]);
$match[5] = str_replace('\\\\', '\\', $match[5]);
}
$var[$match[2]][][$type] = array( 'value' => $match[5],
'comment' => $comment,
'assoc' => (empty($match[3]) ? null : substr($match[3], 1)));
$comment = "";
unset($match);
continue;
}
// section tag (comment)
if (preg_match('!^\[('.$this->regex_section.')\]!', $line, $match)) {
if (trim($match[1]) != '') {
// set previous section vars
$this->_set_param_from_config($var, $section);
$var = array();
$section = trim($match[1]);
} else {
$this->_error("Incorrect section tag on line $line_count");
}
unset($match);
continue;
}
// unrecognized config syntax
if ($this->debug) $this->_debug("$func_name: Unrecognised syntax on line $line_count");
}
$this->_set_param_from_config($var, $section);
}
/**
* Set params from parsed config
*
* Sets parameters for 1 section
*
* @param array $param parameters for 1 section passed from _process_config
* @param string $section section name to enter parameter in
* @return bool true
* @access private
* @see _process_config
*/
function _set_param_from_config($param, $section)
{
foreach ($param as $var => $value) {
// array?
if (count($value) > 1) {
// it is an array
$cur_val = array();
$cur_comment = '';
// loop through the array
foreach ($value as $arr_element) {
// grab the type
$cur_type = key($arr_element);
if (trim($arr_element[$cur_type]['comment']) != '') {
if ($cur_comment != '') {
$cur_comment .= "------------------------------\n";
}
$cur_comment .= $arr_element[$cur_type]['comment'];
}
// is it associative?
if (!is_null($arr_element[$cur_type]['assoc'])) {
// store key
$cur_key = (string)$arr_element[$cur_type]['assoc'];
// add value
$cur_val[$cur_key] = $this->_cast_type($arr_element[$cur_type]['value'], $cur_type);
} else {
// not associative
$cur_val[] = $this->_cast_type($arr_element[$cur_type]['value'], $cur_type);
}
}
} else {
// not array or single assoc array
$cur_type = key($value[0]);
$cur_val = $this->_cast_type($value[0][key($value[0])]['value'], $cur_type);
$cur_comment = $value[0][key($value[0])]['comment'];
$cur_key = $value[0][key($value[0])]['assoc'];
if (!is_null($cur_key)) {
$cur_val = array("$cur_key" => $cur_val);
}
}
$this->set_param($var, $cur_val, $cur_comment, $section);
}
return true;
}
}
?> |