Ankündigung

Einklappen
Keine Ankündigung bisher.

Configuration Management

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Configuration Management

    Hallo!
    Da ich oftmals mehrmals auf Datenbanken zugreifen muss, Teile wie einen Seitentitel oder einen Namen des öfteren verwende und auch ändere, lege ich solche Informationen gerne in einer Konfigurationsdatei ab. Nun habe ich eine Klasse geschrieben, die diese Datei verarbeiten soll. Ich würde mich freuen, wenn ihr mal einen Blick auf diese Klasse werfen könntet und mir eure Meinung dazu sagen könntet. Besonders würde mich interessieren, ob die Klasse eurer Meinung nach alle Aufgaben erfüllt und wie ihr dieses Verfahren findet.
    MfG, Andy

    PHP-Code:
    <?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.3
    * @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($filetrue); 
            }
            
            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($fromfalse);
            }
            
            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.');
            }
        }
    }
    ?>

  • #2
    Hallo Andy,

    ist aus meiner Sicht vollkommen ok. Was ich noch versuchen würde ist, die Klasse singleton zu verwenden, dann muss die Datei nicht bei jedem Aufruf neu geparst werden.

    Was die API angeht, würde ich addItem() und setItem() kombinieren. Die Funktion ist eigentlich identlisch. Ansonsten schönes Beispiel, dass man hier im Forum schon zig Mal hätte verlinken können.

    Zusatz: Was natürlich noch nicht berücksichtigt ist, man aber sehr schön aussen drum basteln kann, ist die Unterscheidung von Konfigurationen. Quasi eine Konvention/Automatismus, mit dem Context- und Umgebungsabhängig Konfigurationen in einem Softwareteil verwendet werden können. Siehst du das nicht vor, musst du u.U. den Quelltext ändern um eine andere Konfiguration zu ziehen.

    Kommentar


    • #3
      Hallo dr.e
      Danke für deine Rückmeldung. Freut mich, dass du soweit einverstanden bist. Die Methode setItem() habe ich um einen Parameter erweitert, sodass ich die Methode addItem() auch dort mit unterbringen konnte. Habe die geänderte Version mal oben eingetragen.

      Ich verstehe leider deinen Einwand nicht. Auf der einen Seite möchtest du, dass ich das Singletone Pattern mit einbaue und auf der anderen Seite fändest du es nett, wenn man mehrere unterschiedliche Configurationen laden könnte. Oder habe ich das falsch verstanden? Das Singletone Pattern hatte ich wie folgt in die Klasse integriert.

      PHP-Code:
      <?php
      class Configuration
      {
          static private 
      $instance NULL;

          static public function 
      getInstance()
          {
              if (
      self::$instance === NULL)
              {
                  
      self::$instance = new self;
              }

              return 
      self::$instance;   
          }

          private function 
      __construct(){}
          private function 
      __clone(){}
      }
      Ist das so ok? Ich habe noch eine Frage zu diesem Thema. Ich habe eine kleine und für mich ausreichende Template-Engine geschrieben. In der Index-Datei instanziere ich diese Engine zusammen mit der Configuration Klasse und einem MySQLi-Objekt. Wie ist es möglich in allen von der index.php aufgerufenen oder von diesen aufgerufenen eingebundenen Dateien diese Instanzen verfügbar zu machen? Hilft mir dabei das Singletone Pattern in Kombination mit dem Registry Pattern? Hätte große Vorteile wenn ich immer auf die selben Instanzen zurückgreifen könnte.

      MfG, Andy

      //EDIT: Sind die Namen der Methoden so ok?

      Kommentar


      • #4
        Hallo Andy,

        ich denke, mit einem Singleton war in deinemF alle ein parametrisiertes Singleton gemeint. Du übergibst der getInstance()-Methode dabei den namen der zu parsenden Datei und anhand dieser wird dann eine neue Instanz erstellt oder eben auf eine schon vorhandene Instanz zurückgegriffen.
        Das könnte z.B. so aussehen:
        PHP-Code:
        class Configuration
        {
            static private 
        $instances = array();

            static public function 
        getInstance($file)
            {
                if (!isset(
        self::$instances[$file])
                {
                    
        self::$instances[$file] = new self($file);
                }

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

            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($filetrue); 
                }
                
                else
                {
                    throw new 
        Exception("{$file} cannot be found.");
                }
            }
            
            protected function 
        __construct()
            {
                
        $this->loadFile($file);
            }
            
            private function 
        __clone(){}

        Ich würde das ganze aber vielleicht sogar eher mit einer abstrakten Klasse Configuration und konkreten Unterklassen XMLConfiguration, INIConfiguration etc. lösen, die dann wiederum wie hier ihre Instanzen pro Datei verwalten.

        Kommentar


        • #5
          Danke für deine Hilfe Manko10. Habe es mal so implementiert, wie du es mir gezeigt hast. Verstehe nun auch, wie dr. e das meinte. Man erlaubt also doch mehrere instanzierungen der Klasse, wobei diese echt unterschieden sei müssen. Das heißt es kann nur dann eine neue Instanz erzeugt werden, wenn noch kein Objekt besteht, dass die gegebene Konfigurationsdatei verarbeitet. Ich stelle also sicher, dass eine bijektive Beziehung zwischen Objekten und Konfigurationsdateien besteht. Super.

          Leider bräuchte ich noch Hilfe bei meiner zweiten Frage. Wie stelle ich sicher, dass in allen während der Laufzeit meines Hauptprogramms aufgerufenen Unterprogrammen, die wichtigsten Objekte, wie ein Konfigurations-, ein MySqli- und ein Template-Engine Objekt verfügbar sind? Ist das mit Hilfe des Singleton und des Registry Pattern sinnvoll lösbar?

          Bin für jede Hilfe dankbar und würde mich auch über weitere Meinungen und Anregungen zu meiner Configuration Klasse freuen.

          MfG, Andy

          //EDIT: Ich habe nochmal eine Frage zum Singleton Pattern. Und zwar wann ist dies sinnvoll und lohnt sich die Mühe es in jede Klasse einzeln zu implementieren oder ist eine Singleton Klasse vollkommen ausreichend. In jeder Klasse für sich implementiert hat es natürlich vorteile, da man zum Beispiel den Konstruktor und die __clone()-Methode als private oder protected deklarieren kann. Zudem würde ich sagen, dass das Singleton Pattern überall dort sinnvoll ist, wo ein einzelnes Objekt vollkommen ausreichend ist. Zum Beispiel in einer Klasse, die Konfigurationsdateien verarbeitet, Eingaben validiert, Rechte verwaltet oder Log-Dateien händelt. Also in fast allen "verwaltenden" Klassen. Seht ihr das genauso?

          Kommentar


          • #6
            Singletons sind generell global verfügbar, da die getInstance()-Methode im Klassenkontext aufgerufen wird.
            Möglich wäre hier z.B. ein abstraktes Singleton, wie es der Doc auch in seinem APF verwendet.

            Kommentar


            • #7
              Danke für deine Antwort. Ich habe mal schnell eine solche abstrakte Singleton Klasse geschrieben (Code steht unten). Um diese zu nutzen müsste ich also nun alle Klassen, für die das Singleton Pattern sinnvoll ist erben lassen. Nun treten aber mehrere Probleme auf. Was ist mit Klassen, wie der Configuration Klasse, die zwar das Singelton Pattern nutzen dennoch mehrfach instanziert werder können soll. Was ist außerdem mit Klassen, wie der MySqli Klasse. Dort wäre es auch sehr praktisch nur eine Instanz zu haben, doch dort kann ich das Pattern nicht einfach so implementieren. Hilft mir hier das Registry Pattern? Beim registry Pattern werden ja einfach alle Instancen in einem großen Array gespeichert. So sind sie einfach ansprechbar und man kann ermeiden, dass es unnütze Instanzierungen gibt.
              MfG, Andy

              PHP-Code:
              <?
              error_reporting(E_ALL);

              /***
              * Abstract class Singleton 
              *
              * Singleton class makes sure that
              * there is just one instance
              * of the succeeding classes.
              *  
              * @package Singleton
              * @version 0.1
              * @author Andreas Wilhelm <Andreas2209@web.de>
              * @copyright Andreas Wilhelm
              **/  
              abstract class Singleton 
              {
                  // instance counter
                  static private $instance = NULL;

                  /**
                  * getInstance() - Creates new instance
                  *
                  * @access public
                  * @return NONE
                  */
                  static public function getInstance()
                  {
                      if (self::$instance === NULL)
                      {
                          self::$instance = new self;
                      }

                      return self::$instance;   
                  }

                  /**
                  * __construct() - External access blocked
                  *
                  * @access private
                  * @return NONE
                  */
                  private function __construct(){}

                  /**
                  * __clone() - External access blocked
                  *
                  * @access public
                  * @return NONE
                  */
                  private function __clone(){}
              }
              ?>

              Kommentar


              • #8
                Hallo Andy,

                für diese Fälle habe ich einen ConfigurationManager (Factory), der die unterschiedlichen Konfigurationen verwaltet. Die Factory ist ein Singleton und damit auch die unterschiedlichen Instanzen.

                Was das Thema Singleton angeht, würde ich - wie auch schon von Manko10 angesprochen - ein generisches Singleton verwenden und nicht jede Klasse von einer Singleton-Klasse ableiten lassen. Vorteil der Lösung ist, dass du jede Klasse als solche behandeln kannst und nicht kein "Mehrfachvererbung in PHP geht nicht"-Problem hast, wenn eine Klasse von einer anderen ableiten sollte.

                Ich hoffe, damit sind deine Fragen beantwortet. Wenn du Anregungen brauchst, schau mal unter Adventure PHP Framework - Konfiguration vorbei. Das ist die Seite, die Manko10 angesprochen hat.

                Kommentar


                • #9
                  Danke für deine Hilfe dr.e. Verstehst du unter "generischem Singleton" nun eine Singleton Klasse (nicht abstrakt) oder meinst du damit, dass in jede Klasse für sich das Singleton Pattern implementiert werden muss?
                  MfG, Andy

                  //EDIT: Habe noch ein Problem. Ich instanziere verschiedene Klassen in meiner Index-Datei. In dieser Index-Datei include ich auch eine Datei. Dort möchte ich die Klasse Validator nutzen, die zuvor via
                  PHP-Code:
                  $valid Validator::getInstance(); 
                  instanziert wurde. Ich grefe in der includeten Datei doch ebenso wie ich auch die Klasse instanziere auf die Klasse zu? Ist das richtig? Wäre nett, wenn mir dabei auch noch jemand helfen könnte.

                  Kommentar


                  • #10
                    a) eine Klasse, welche ähnlich wie eine Fabrik agiert und die Instanzen der verschiedenen Klassen verwaltet. Dabei kann diese Klasse durchaus abstrakt sein, da von ihr keine Instanzen abgeleitet werden (die Methoden sind hingegen konkret).

                    b) ich verstehe nicht ganz, was du meinst.
                    Wenn du aus einer Include-Datei auf die Klasse zugreifst ist das dieselbe Klasse wie in der Hauptdatei, ja. Oder was meinst du?

                    Kommentar


                    • #11
                      Ah ich hab es verstanden. Mit generischem Sigleton meint er also eine Klasse, die wie folgende aufgebaut ist.

                      PHP-Code:
                      <?php

                      class Singleton
                      {
                          static private 
                      $instances = array();

                          static public function 
                      getInstance($class)
                          {
                              if( !isset(
                      self::$instances[$class]) )
                              {
                                  
                      self::$instances[$class] = new $class();
                              }

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

                      require_once(
                      'Configuration.php');
                      $config Singleton::getInstance('Configuration');

                      ?>
                      Diese Methode hat allerdings den Nachteil, dass man das Instanzieren nicht durch private unterbinden kann. Wäre dies möglich, wäre eine solche Klasse die optimale Lösung. Wie man das allerdings in eine abstrakte Klasse packen kann und wie man diese ohne Vererbung verwenden soll, weiß bzw. versteh ich nicht.

                      MfG, Andy

                      Kommentar


                      • #12
                        Erst mal Hallo zusammen..

                        Was spricht eigentlich dagegen die Config gleich als Klasse anzulegen, die nur konstanten bzw. statische Variablen enthält?

                        PHP-Code:
                        class Config{
                          const 
                        einstellung 'wert';
                        }

                        echo 
                        Config::einstellung
                        Ich kann noch nicht ganz nachvollziehen weshalb man dieses Theater mit dem Singleton-Pattern macht, wenns auch so einfach geht. Also wenn man mehrere Instanzen in einem Array hat, dann sehe ich den Sinn, aber wenn man sowieso nur eine einzige Instanz erlaubt, kommt es doch auf dasselbe heraus, nur dass das so viel einfacher ist.

                        Vielleicht kann mich ja jemand aufklären, wieso man das nicht so macht.

                        Kommentar


                        • #13
                          Hallo Bleda,

                          Klassen sind Code und in kompilierbaren Sprachen - auch PHP zählt dazu - nicht mehr veränderbar. Damit ist die Konfiguration via Klassen nutzlos. Da eine Konfiguration variable Daten enthält sollten diese in einem "veränderbaren" Format (zumeist Text-Dateien) gespeichert werden.

                          Was das Singleton- bzw. Factory-Thema angeht, so haben wir - im groben gesprochen - über flexible Konfiguration, Performance und Wiederverwendbarkeit gesprochen. Ich würde dir empfehlen, mal ein Buch über Design-Pattern zu konsultieren. Es mag im ersten Moment sicher wie Overhead aussehen, es ist jedoch nur die Anstrengung, wiederverwendbare und generisch einsetzbare Software zu schreiben. Quasi nach dem DRY-Prinzip.

                          Kommentar


                          • #14
                            Ganz einfach. Erklär mir doch mal, wie man bei deiner Methode Daten ändern, löschen, sortieren oder ähnliches machen möchte. Zudem soll das Singleton Pattern nur verhindern, dass es mehrere Objekte zur gleichen Zeit gibt. Zudem erspart es teilweise das Einbinden und instanzieren anderer Klassen.
                            MfG, Andy

                            //EDIT:
                            Gibt es denn eine Methode, eine Art eine Singleton Klasse aufzubauen, bei der das Instanzieren durch private unterbunden werden kann? Eine Singleton Klasse hatte ich oben ja bereits gepostet.

                            Kommentar


                            • #15
                              Im Falle des generischen Singletons ist das Unterbinden einer Fremdinstantiierung nicht möglich, da auch das Singleton eine Fremdklasse ist. Das mag Nachteile bringen, allerdings auch Vorteile, da es die Flexibilität steigert. Weil die Instantiierung wieder mehr in der Verantwortung des Programmierers liegt, sollte hier allerdings mit guter Dokumentation gearbeitet werden.
                              Ab PHP 5.3 wird ein Abstract Singleton dieser Art möglich sein: Abstract Singleton - php bar, jedoch sehe ich darin einen anderen Ansatz. Während bei dem generischen Singleton, das wir hier haben, das Ziel ist, die Klasse selbst komplett frei von Singleton-Krams zu halten, wird dort wieder auf Vererbung gesetzt. Die getInstance()-Methode muss zwar nicht mehr in jeder Klasse neu implementiert werden, aber dennoch muss eine Vererbung stattfinden, was wiederum bedeutet, dass die Klasse von keiner anderen Klasse mehr erben kann (Stichwort: Mehrfachvererbung).

                              Kommentar

                              Lädt...
                              X