Konfigurations-Management

  • Themenstarter Themenstarter Bgag
  • Beginndatum Beginndatum
B

Bgag

Hallo zusammen.
Ich habe mich schon seit längerem mit dem Problem des Konfigurations-Managements beschäftigt. Dabei habe ich mir verschiedene Ansätze überlegt und auch getestet. Die erste und auch einfachste Idee - eine PHP-Datei voller Variabeln. Der Nachteil ist, dass man nicht ohne weiteres automatisch neue Punkte hinzufügen, alte löschen oder ganz einfach vorhandene editieren kann. Dadurch fällt diese Option weg.

Mein nächster Ansatz baute auf *.ini-Dateien, die relativ komfortabel mit parse_ini_file() ausgelesen werden können. Leider stellt sich auch hier das Problem des editierens. Alle HTML-Codes mit Attributen stellen ein Problem dar, da parse_ini_file() gequotete Bereiche besonders behandelt. Zudem werden oftmals Kommentare beim schreiben der *.ini-Datei gelöscht, da dies mit einer eigenen Funktion geschehen muss.

Ich persönlich habe mich für die dritte Methode entschieden. Diese arbeitet ebenfalls mit *.ini-Dateien, baut allerdings nicht auf parse_ini_file() auf. viel mehr wird hier mal wieder auf RegExp gesetzt. Meine Klasse Config.php ist zwar noch nicht ganz fertig, da die Methoden zum Anlegen und erweitern von Konfigurations-Dateien fehlen, aber das wird sich noch ändern. Leider ist nun ein kleines Problem aufgetreten. Und zwar versuche ich mit Hilfe der magischen Methode __set() einem Key des $config-Arrays ein neues Value zuzuordnen. Dies soll in der Datei editconfig.php, die den Klassen-Aufruf enthält automatiesiert in einer Schleife, die mit $_REQUEST[]-Daten gefüttert wird, geschehen. Dort liegt auch der Fehler. Die Funktion wirft eine Exception, da der Key, der gesetzt werden soll, nicht existiert. In dieser Datei sollen nämlich alle in der Konfigurations-Datei enthaltenen Keys aktualisiert werden, wozu sie auf ihre Existenz überprüft werden. Leider wird dabei hinter alle Keys ein "_" gesetzt, wodurch aus dem Key "dbname", der Key "dbname_" wird, der natürlich nicht gefunden wird und der Fehler wird geworfen.

Fehler: Liegt in dem Aufruf der in der if()-Abfrage gestartet wird.
PHP:
      foreach( $new as $key => $value)
      {
         if( ($key != 'reset') && ($key != 'submit') && ($key != 'path') )
         {
            $cp->$key = $value;
         }
      }

Frage: Wie muss ich den Aufruf abändern, damit dieser Fehler nicht weiter auftritt, oder ist es einfach sinnvoller diesen Fehler einfach zu umgehen, indem ich auf die Verwendung der magischen Methoden __set() und __get() verzichte?

Wäre euch sehr sehr dankbar für eure Hilfe!
MfG, Andy


Config.php
PHP:
<?php
error_reporting(E_ALL);

/***
* The Config class allows reading the configuration and editting it
*  
* @package Config
* @version 0.1
* @author Andreas Wilhelm <Andreas2209@web.de>
* @copyright Andreas Wilhelm
**/  
class Configuration
{
	private $file;
	private $config = array();

	/**
	* Cunstructor - Loads configuration from path
	*
	* @access public
	* @param Str $file
	* @return NONE
	*/
	public function __construct($file) 
	{
		// set path to config-file
		$this->file = $file;
		
		// save configuration to an array
		$this->parse();
	}

	/**
	* __get() - Gets the value of an config-item
	*
	* @access: public
	* @param Str $name
	* @return String
	*/
	public function __get($key) 
	{ 
		if( !isset($this->config[$key]) )
		{
			throw new Exception('Cannot get item '.$key.'.');
		}
		
		return $this->config[$key]; 
	}
	
	/**
	* __set() - Assigns a new value to an entry of the config
	*
	* @access: public
	* @param Str $key
	* @param Str $value
	* @return NONE
	*/
	public function __set($key, $value) 
	{		
		if( !isset($this->config[$key]) )
		{
			throw new Exception('Cannot set item '.$key.'.');
		}
		
		$this->config[$key] = $value; 
	}
    
	/**
	* getConfig() - Returns the whole configuration in an array
	*
	* @access: public
	* @return Array
	*/
	public function getConfig()
	{
		return $this->config;
	}

	/**
	* parse() - Reads configuration into an array
	*
	* @access: public
	* @return NONE
	*/
	public function parse()
	{
		// save each line of data to an array-element
		$lines = file($this->file);
                        
		foreach($lines as $line => $value)
		{
			// save information to array
			if( preg_match('/([^:]+)=(.+)/m', $value, $match) )
			{
				$this->config[$match[1]] = $match[2];
			}
		}
	}
	
	/**
	* save() - Save Configuration to file
	*
	* @access: public
	* @return NONE
	*/
	public function save()
	{
		$nf = '';
		$fp = fopen( $this->file, 'r' );
		while( $line = fgets($fp) )
		{
			if ( preg_match('/([^:]+)=(.+)/m', $line, $match) )
			{
				$nf .= $match[1]."=".$this->config[$match[1]]."\n";
			}
			
			else
			{
				$nf .= $line;
			}
		}		
		fclose( $fp );
		
		$fp = fopen( $this->file, 'w' );
		fwrite( $fp, $nf );
		fclose( $fp );
	}
}
?>

config.ini
Code:
# database values
dbhost = your_database_host
db = your_db
dbuser = testuser
dbpwd = testpwd

# meta tags
title = title of your website
author = the author of the website
copyright = copyright by
discription = this is a great website
keywords = website, your, nice
favicon = favicon.ico

# error pages
error400 = Error 400 - Bad request.
error404 = Error 404 - Page could not be found.
error500 = Error 500 - Internal Server Error.

editconfig.php
PHP:
	$cp = new Configuration('config/config.ini');
  
	// handle request-data
	if( isset($_REQUEST) )
	{
		$new = $_REQUEST;
    
		foreach( $new as $key => $value)
		{
			if( ($key != 'reset') && ($key != 'submit') && ($key != 'path') )
			{
				$cp->$key = $value;
			}
		}
    
		$cp->save();
	}
	
	// get changed configuration
	$config = $cp->getConfig();
 
Also die Idee und den Ansatz find ich ja erstma genial ^^

Deine __set() erwartet 2 Parameter, aber bekommt nur einen, wenn ich das richtig sehe...

Ich würde aber einfach darauf verzichten und die __ weglassen... Also letztlich die Funktion selbst aufrufen und nich darauf verlassen, dass da was automatisch richtig hin haut...

Hieße also:
PHP:
foreach( $new as $key => $value)
  {
     if( ($key != 'reset') && ($key != 'submit') && ($key != 'path') )
    {
       $cp->set($key, $value);
    }
 }
 
Die einzige gute Lösung, bei der man am wenigsten Geschwindigkeit einbüst, ist die Konfiguration über eine .php Datei; und ob du nun eine .ini Datei neu generierst oder eine .php Datei ist relativ egal.
 
Wenn ich das richtig verstanden habe, geht es erstmal darum wo dieser Unterstrich herkommt, oder? Denn das ist doch die Wurzel allen übels, weshalb dann auch die __set() Methode die Exception wirft.

Hast du einfach mal alle relevanten $key Variablen per echo ausgegeben?
Beim dem gezeigten Code konnte ich nichts erkennen, das dort ein "_" generiert wird und die __set() Methode erzeugt diesen Unterstrich auch nicht magisch im Hintergrund.

Es könnte auch sein, das ich den Fehler total falsch verstanden habe, denn alle antworten behandeln hauptsächlich die __set() Methode, was meinem Verständniss nach nur der Punkt ist, wo der Fehler sichtbar wird.

Gruss
 
Danke erstmal für eure Hilfe. Felix hat Recht RegExp sind für eine solche klasse zu langsam. die Operationen werden einfach zu häufig durchgeführt, sodass man so etwas, auch wenn die Konfigurations-Dateien nicht lang sind, schon sehr merken kann. Gegen XML habe ich mich sehr gesträubt finde ich unsympatisch und es treten auch dort die schon genannten Probleme auf. Die JSON Funktionen haben es mir aber angetan. ich habe einfach mal so aus Spaß etwas damit herum gespielt und war eigentlich sehr begeistert von den beiden Funktionen json_decode() und json_encode(). Vorallem ist dabei sowohl das editieren, als auch das erstellen vollkommen neuer Dateien sehr sehr einfach. Doch leider kann nicht alles Perfekt sein. Die Funktion json_encode() maskiert leider nicht die doppelten Anführungszeichen, wodurch es zu einem Fehler kommt, möchte man zum Beispiel einen Link-Code in der Konfigurations-Datei speichern. Den fehler sieht man hier. Das Script habe ich mal angehängt. hat irgendjemand eine Idee, wie ich dieses Problem lösen könnte? Ein dem json_encode() vorangestelltes addslashes() löst leider das Problem nicht, da dann zum Beispiel Backslashes doppelt maskiert werden.
MfG, Andy

PHP:
<pre>
<?php
error_reporting(E_ALL);

	$json = '{
		"db_host" : "localhost",
		"db_name" : "my_db",
		"db_user" : "klaus",
		"db_pwd" : "hutab",
		"error404" : "<a href=\"http:\/\/www.avedo.net\">Hier<\/a> gehts zur <b>Startseite<\/b>."}';
	
	var_dump(json_decode($json));
	var_dump(json_decode($json, true));
	
	$config = array(
		'db_host' => 'localhost',
		'db_name' => 'my_db',
		'db_user' => 'klaus',
		'db_pwd' => 'hutab',
		'error404' => '<a href="http://www.avedo.net">Hier</a> gehts zur <b>Startseite</b>.');
		
	echo json_encode($config);
?>
<pre>
 
{"db_host":"localhost","db_name":"my_db","db_user":"klaus","db_pwd":"hutab","error404":"<a href=\"http:\/\/www.avedo.net\">Hier<\/a> gehts zur <b>Startseite<\/b>."}

So sieht die Ausgabe bei mir aus... Und das sieht für mich ziemlich richtig aus?
Bei mir kommt PHP 5.3.0-dev zum Einsatz...
 
Bei mir wird es leider nicht so ausgegeben, wie du vielleicht gesehen hast. Auf meinem Server läuft PHP Version 5.2.0-8+etch10. Wie du siehst fehlt der Teil <a href="deine-seite"> und es ist nur Hier </a> als Link zu sehen. Also nicht wirklich das, was ich will. Woran kann das liegen? Ist die PECL (The PHP Extension Community Library) ab 5.2.0 nicht standradgemäß implementiert?
MfG, Andy
 
Hallo!
Ich habe mich nun nach längerer Zeit mal wieder mit diesem Thema beschäftigt und würde nun gerne wissen, was ihr von meiner Klasse haltet. Habe komplett neu geschrieben, weil mir der alte Ansatz nicht gefallen hat. Wäre dankbar für eine kurze Stellungnahme und weitere Anregungen.
MfG, Andy

// UPDATE: Klasse auf Singletone umgestellt. Es wird nun sichergestellt, dass zu jeder Konfigurationsdatei nur ein Objekt besteht.

PHP:
<?php

/***
* Class Configuration
* 
* The Configuration class allows reading a configuration-file.
* Furthermore you are able to add new entries to the configuration.
* Sure it is also possible to add, remove, edit, query and to save
* the new configuration. Finally it is important to 
* mention that this class supports the structuring of the 
* configuration in different sections, which sure can also
* be handled. Warning! This class can just handle existing 
* configuration files.
* 
* @package Configuration
* @version 0.2
* @author Andreas Wilhelm <Andreas2209@web.de>
* @copyright Andreas Wilhelm 
**/  
class Configuration
{
	// instances
	static private $instances = array();
	
	// private class-variables
	private $config = array();
	private $file;
	
	/**
	* getInstance - Creates a new instance
	*
	* @access public
	* @param Str $file
	* @return NONE
	*/
	static public function getInstance($file)
	{
		if (!isset(self::$instances[$file])
		{
			self::$instances[$file] = new self($file);
		}

		return self::$instances[$file];   
	}

    /**
	* loadFile() - Loads configuration from path
	*
	* @access protected
	* @param Str $file
	* @return NONE
	*/
	protected function loadFile($file)
    {
        if( file_exists($file) )
        {
            // set path to config-file
            $this->file = $file;
        
            // save configuration to an array
            $this->config = parse_ini_file($file, true); 
        }
        
        else
        {
            throw new Exception("{$file} cannot be found.");
        }
    }
    
    /**
	* Constructor - Is called when the class is instanced
	*
	* @access protected
	* @param Str $file
	* @return NONE
	*/
	protected function __construct($file)
    {
        $this->loadFile($file);
    }
    
    /**
	* __clone() - Is blocked
	*
	* @access private
	* @return NONE
	*/
	private function __clone() {}
    
	/**
	* delItem() - Removes an item
	*
	* @access: public
	* @param Str $section
	* @param Str $key
	* @return NONE
	*/
	public function delItem($section, $key)
	{
		if( isset($this->config[$section][$key]) )
		{
			unset($this->config[$section][$key]);
		}
		
		else
		{
			throw new Exception("Cannot remove {$section} - {$key}.");		
		}
	}
	
	/**
	* setItem() - Assigns a new value to an entry of the config
	*
	* @access: public
	* @param Str $section
	* @param Str $key
	* @param Str $value
	* @param Bool $overwrite
	* @return NONE
	*/
	public function setItem($section, $key, $value, $overwrite = false) 
	{
		if( $overwrite === true )
		{
			$this->config[$section][$key] = $value;
		}
		
		elseif( !isset($this->config[$section][$key]) )
		{
			$this->config[$section][$key] = $value;
		}
		
		else
		{
			throw new Exception("Item {$section} - {$key} already exists.");
		}
	}

	/**
	* getItem() - Gets the value of an config-item
	*
	* @access: public
	* @param Str $section
	* @param Str $key
	* @return String
	*/
	public function getItem($section, $key) 
	{ 
		if( !isset($this->config[$section][$key]) )
		{
			throw new Exception("Cannot get item {$section} - {$key}.");
		}
		
		return $this->config[$section][$key]; 
	}
    
	/**
	* rnItem() - Renames an item
	*
	* @access: public
	* @param Str $section
	* @param Str $from
	* @param Str $to
	* @return NONE
	*/
	public function rnItem($section, $from, $to)
	{
		if( isset($this->config[$section][$from]) && !isset($this->config[$section][$to]))
		{
			// move data to new section
			$this->config[$section][$to] = $this->config[$section][$from];
			
			// remove old section
			$this->delItem($section, $from);
		}
		
		else
		{
			throw new Exception("Cannot rename item {$section} - {$from}.");
		}
	}
    
	/**
	* addSection() - Adds a section
	*
	* @access: public
	* @param Str $name
	* @return NONE
	*/
	public function addSection($name)
	{
		if( !isset($this->config[$name]) )
		{
			$this->config[$name] = array();
		}
		
		else
		{
			throw new Exception("Section {$name} already exists.");
		}
	}
    
	/**
	* delSection() - Deletes a section
	*
	* @access: public
	* @param Str $name
	* @param Boo $ifEmpty
	* @return NONE
	*/
	public function delSection($name, $ifEmpty = true)
	{
		if( isset($this->config[$name]) )
		{
			if( ($ifEmpty == true) && (count($this->config[$name]) > 0) )
			{
				throw new Exception("Section {$name} is not empty.");
			}
			
			else
			{
				unset($this->config[$name]);
			}	
		}
		
		else
		{
			throw new Exception("Cannot found section {$name}.");
		}
	}
    
	/**
	* getSection() - Returns all items of a section
	*
	* @access: public
	* @param Str $name
	* @return Array
	*/
	public function getSection($name)
	{
		$items = array();
		
		foreach( $this->config[$name] as $key => $value )
		{
			$items[$key] = $value;
		}
		
		return $items;
	}
    
	/**
	* getSections() - Returns all sections
	*
	* @access: public
	* @return Array
	*/
	public function getSections()
	{
		$sections = array();
		
		foreach( $this->config as $key => $value )
		{	
			if( is_array($value) )
			{
				$sections[] = $key;
			}
		}
		
		return $sections;
	}
    
	/**
	* rnSection() - Renames a section
	*
	* @access: public
	* @param Str $from
	* @param Str $to
	* @return NONE
	*/
	public function rnSection($from, $to)
	{
		if( isset($this->config[$from]) && !isset($this->config[$to]))
		{
			// move data to new section
			$this->config[$to] = $this->config[$from];
			
			// remove old section
			$this->delSection($from, false);
		}
		
		else
		{
			throw new Exception("Cannot rename section {$from}.");
		}
	}
    
	/**
	* getConfig() - Returns the whole configuration in an array
	*
	* @access: public
	* @return Array
	*/
	public function getConfig()
	{
		return $this->config;
	}
    
	/**
	* setConfig() - Creates a new Configuration from array
	*
	* @access: public
	* @param Arr $new
	* @return Boolean
	*/
	public function setConfig($new)
	{
		$this->config = $new;
	}
	
	/**
	* save() - Save Configuration to file
	*
	* @access: public
	* @return Boolean
	*/
	public function save()
	{
		$config = "; <?php die('Blocked unwanted request.'); ?>\n";
	
		foreach( $this->config as $key => $value)
		{
			if( is_array($value) )
			{
				// save section name
				$config .= "[$key]\n";
				
				// save section items
				foreach( $value as $k => $v)
				{
					$config .= "$k = \"$v\"\n";
				}
			}
			
			else
			{
				// save item
				$config .= "$key = \"$value\"\n";
			}
		}
		
		echo $config;
        
        if( !file_put_contents($this->file, $config) )
        {
			throw new Exception('Cannot save configuration.');
        }
	}
}
?>
 
Zuletzt bearbeitet von einem Moderator:
Zurück