TobiasBuschi: PHP5 Klassenvariable

hallo allerseits

Für ein Datenbank-Tool will ich eine Klasse, die "einmal!" die gesamte Struktur der Datenbank ermittelt.
Diese Struktur ist dann in einem Array gespeichert:
$struct[tabelle][feld].

Meine Klasse:

class DBM{
   var DBstruct = array();

__construct(){
     DBstruct = DB_Struct_ermitteln();
   }

function DB_Struct_ermitteln(){
      return struktur_der_Datenbank;
   }
}

jedesmal wenn ich ein Objekt von der Klasse DBM erstelle wird die Struktur unsinnigerweise neu ermittelt.

Mit einer Globalen Variable könnte ich das lösen indem ich abfrage ob die Variable schon existiert, dann gebe ich sie einfach zurück. Das will ich jedoch nicht weil es nicht so sauber ist.

Auch sollte in vererbten Klassen das Array vorhanden sein.

Geht das irgendwie mit statischen Variablen??

mfg Tobias Buschor

  1. jedesmal wenn ich ein Objekt von der Klasse DBM erstelle wird die Struktur unsinnigerweise neu ermittelt.

    Das Stichwort lautet hier Singleton! Ein Singleton ist eine Klasse, von der nur eine Instanz existieren kann:

    class DBM
    {
       private $DBstruct = array();
       static private $instance = null;

    protected function __construct()
       {
         $this->DBstruct = DB_Struct_ermitteln();
       }

    // __clone nicht ausführbar machen
       final private function __clone() {};

    public function DB_Struct_ermitteln()
       {
          return struktur_der_Datenbank;
       }

    static public function singleton()
       {
          if ( is_null( self::$instance ) )
          {
             self::$instance = new DBM();
          }

    return self::$instance;
       }
    }

    Eine Instanz erstellst Du so:

    $dbm = DBM::singleton();

    Wenn Du nun die statische Methode singleton() erneut aufrufst, wird nicht noch eine Instanz der Klasse erstellt, sondern es wird eine Referenz auf die vorhandene Instanz zurückgeliefert. Dadurch, dass der Konstruktor protected ist, kannst Du nicht direkt eine Instanz erstellen. Ein Objekt kann ebenfalls nicht geklont werden, da die __clone() Methode leer ist, private und als finale deklariert.

    Das Sprachelement "var" solltest Du in PHP 5 übrigens nicht mehr verwenden.

    1. Die Methode DB_Struct_ermitteln() sollte natürlich besser protected oder private sein. Am besten erstellst Du noch eine öffentliche Methode getDBStruct(), die dann die Eigenschaft DBstruct zurückliefert.

    2. Danke, sehr hilfreich deine Infos.

      Noch eine Frage:
      Von dieser Klasse kann ich nicht eine andere Klasse ableiten wo ich diese Array (und andere Eigenschaften/Methoden) zur Verfügung habe? $DBstruct ist  "private"!

      Ist es so, dass in einer abgeleiteten Klasse der "parent::constructor" automatisch auch aufgerufen wird?

      Wie soll ich vorgehen?
      1. Eine Instanz von DBM einmal global erstellen und in den Klassen welche die DB-Struktur benötigen, auf das globale Objekt zugreifen

      2. Eine Instanz von DBM einmal erstellen und den Klassen welche die DB-Struktur benötigen, das Objekt als Parameter übergeben

      3. Irgendwie mit Vererbung??

      mfg tobi

      1. Kann mir bitte noch einer Die Fragen beantworten, bitte :)

        Ist es so, dass in einer abgeleiteten Klasse der "parent::constructor" automatisch auch aufgerufen wird?

        und noch was:

        Kann man mit PHP auch Methoden Ausserhalb der Klasse Definieren, so wie in C++ myclass::get_was(){}
        so irgendwie.

        mfg
        Tobias Buschor

        1. Kann mir bitte noch einer Die Fragen beantworten, bitte :)

          Ist es so, dass in einer abgeleiteten Klasse der "parent::constructor" automatisch auch aufgerufen wird?

          Nein, in PHP 5 musst du den Konstruktor des Vaters selber aufrufen.

          und noch was:

          Kann man mit PHP auch Methoden Ausserhalb der Klasse Definieren, so wie in C++ myclass::get_was(){}
          so irgendwie.

          Nein.

    3. echo $begrueszung;

      jedesmal wenn ich ein Objekt von der Klasse DBM erstelle wird die Struktur unsinnigerweise neu ermittelt.

      Das Stichwort lautet hier Singleton!

      Ähmm, nein.

      Ein Singleton ist eine Klasse, von der nur eine Instanz existieren kann:

      Und deshalb ist singleton hier für den OP nicht die richtige Herangehensweise, denn er will ja mehrere Instanzen haben, und das ist durchaus sinnvoll.

      Aber so wie du das mit der $instance und der singleton()-Methode machst, kann man das mit der DB-Struktur und der Methode, die die Struktur liefert, machen. Sprich:

      class... {
        private static $DBStruct = null;

      function getStruct() {
          if (is_null($DBStruct))
            $DBStruct = initialisierung...
          return $DBStruct;
        end;
      }

      An den OP:
      Du willst die Struktur in $DBStruct als Array speichern. Arrays werden in PHP5 (immer noch) by Value (als Kopie) und nicht als Referenz zurückgegeben. Wenn du

      function &getStruct() {

      (beachte das &) nimmst, wird jetzt eine Referenz auf $DBStruct übergeben, das ist zwar schneller als ein Kopieren, hat aber den Nachteil, dass jeder der getStruct() aufrufen kann nun den Inhalt von $DBStruct verändern kann.

      echo "$verabschiedung $name";

      1. Danke, wieder einiges dazugelernt.

        Sowas habe ich auch schon probiert, ich habe gedacht das geht nicht, aber ich schaus mir nochmals an.

        mfg
        Tobias Buscbor

      2. Funktioniert leider nicht, vieleicht ist das ein Bug!?

          
        class rpDBM{  
         private static $dbStruct = null;  
         private static $dbRels   = null;  
          
         function connect(){  
          if( connect_zur_db() )        } else {  
            $return = false;  
           }  
          } else {  
           $return = false;  
          }  
          
          if($return){  
           $this->get_dbStruct(); // hier  
           $this->get_dbRels();  
          } else {  
          }  
          return $return;  
         }  
          
         private function init_dbStruct(){  
                                echo "halllllllloooo" // wird zweimal ausgegeben!!  
                                code...  
           $this->dbStruct = $struct;  
           return $struct;  
         }  
         private function init_dbRels(){  
                                code...  
           $this->dbRels = $rels;  
           return $rels;  
         }  
        }  
          
        class rpDBM_relations extends rpDBM{  
         function check($display){  
          $this->checkNoField($display);  
          $this->checkNoPair($display);  
          $this->checkDouble($display);  
         }  
          
         function checkNoField($display = true){  
          $struct = $this->get_dbStruct(); // hier  
          foreach($struct AS $table => $fields){  
           foreach($fields AS $field => $f_opt){  
            echo $table.'<br />';  
            echo $field.'<br />';  
           }  
          }  
        //  $this->query();  
         }  
        }  
          
        
        

        mfg
        Tobias Buschor

        1. echo $begrueszung;

          Funktioniert leider nicht, vieleicht ist das ein Bug!?

          Bestimmt. Nur, wer hat ihn gemacht? :-) Viele Fehler sitzen zwischen Tastatur und Rückenlehne... :-)

          function connect(){
            if( connect_zur_db() )        } else {

          Dass du hier irgendwie falsch klammerst, ist dir bestimmt schon aufgefallen.

          [...]

          if($return){
             $this->get_dbStruct(); // hier

          In der Klasse sehe ich keine Deklaration der Methode get_dbStruct(). Auch ist sie eine Basis-Klasse, spricht: sie extended keine andere, von der sie diese erben könnte.

          Ansonsten bitte ich um genaue Angaben, was nicht funktioniert (inklusive Fehlermeldungstext).

          echo "$verabschiedung $name";

          1. Ich hoffe du findest dich im Code zurecht.
            Es gibt keine Fehlermeldung, aber es kommt 2 mal "halllllloooo" auf den Bildschirm, das heist die Struktur der DB wird in der Funktion "get_dbStruct" zwei mal ermittelt.

            Ich dachte eigentlich (du wahrscheindlich auch), dass die statisch deklarierte Variable $dbStruct in der abgeleiteten Klasse auch noch mit dem Initialisierten Wert von der rpDBM Klasse vorzufinden ist, dem ist leider nicht so, sie hat wieder den Wert "null" :(

              
            // Datenbank-Management  
            class rpDBM{  
             private static $dbStruct = null;  
             private static $dbRels   = null;  
              
             public function get_dbStruct(){ // tabellen/felder  
              if( is_null($this->dbStruct) ){  
               return $this->init_dbStruct();  
              }  
              return $this->dbRels;  
             }  
             public function get_dbRels(){ // beziehungen  
              if( is_null($this->dbRels) ){  
               return $this->init_dbRels();  
              }  
              return $this->dbRels;  
             }  
              
             function connect(){  
              if( $this->dbConnect = mysql_connect('localhost','root','') AND $this->dbSelected = mysql_select_db(rpdbm) ){  
               if( @mysql_query("SELECT version FROM rpa_info WHERE id = '1'") ){ // Datenbank kompatibel?  
                $return = true;  
               } else {  
                $return = false;  
               }  
              } else {  
               $return = false;  
              }  
              
              if($return){  
               $this->get_dbStruct(); // 1. Aufruf  !!!!!!!!  
               $this->get_dbRels();  
              } else {  
              }  
              return $return;  
             }  
              
             private function init_dbStruct(){  
               echo "halllllllllo";  
               $e_res = mysql_list_tables('rpdbm');  
               while($e = mysql_fetch_row($e_res)){  
                $e_n = $e[0];  
              
                $a_res=mysql_query("SHOW FIELDS FROM `$e_n`");  
                while($a = mysql_fetch_array($a_res, 1)){  
                 $a_n = $a['Field'];  
              
                 $struct["$e_n"]["$a_n"] = $a;  
                }  
               }  
               $this->dbStruct = $struct;  
               return $struct;  
             }  
             private function init_dbRels(){  
               $query = "  
               SELECT  
               e0.id  AS `id`, e0.entity  AS `from_e` , e0.field  AS `from_a` ,  e0.typ  AS `typ`, e0.del_cas AS `del_cas`, e0.clone_cas AS `clone_cas`,  
               e53.id AS `r_id`, e53.entity AS `e`, e53.field AS `a`,  e53.typ AS `r_typ`  
               FROM  
               `rpa_relations` AS e0  ,`rpa_relations` AS e53  
               WHERE  
               e53.id    = e0.relation_id  
               ";  
              
               $rel_res = mysql_query($query);  
               while ( $db_rels = mysql_fetch_array($rel_res, 1) ){  
                $id = $db_rels['id'];  
                $e  = $db_rels['from_e'];  
                $a  = $db_rels['from_a'];  
              
                unset($db_rels['id']);  
                unset($db_rels['from_e']);  
                unset($db_rels['from_a']);  
                unset($db_rels['log_id']);  
                unset($db_rels['log_id_ch']);  
              
                $rels["$e"]["$a"]["$id"] = $db_rels;  
               }  
               $this->dbRels = $rels;  
               return $rels;  
             }  
            }  
              
            class rpDBM_relations extends rpDBM{  
             public function checkNoField($display = true){  
              $struct = $this->get_dbStruct(); // 2. Aufruf !!!!!!  
              foreach($struct AS $table => $fields){  
               foreach($fields AS $field => $f_opt){  
              
               }  
              }  
             }  
            }  
              
            $dbm = new rpDBM;  
            $dbm->connect();  
              
            $test_rel_check = new rpDBM_relations;  
            $test_rel_check->checkNoField();  
            
            
            1. echo $begrueszung;

              Ich hoffe du findest dich im Code zurecht.

              Ehrlich gesagt hab ich mir nur den Anfang angeschaut :-) (siehe unten)

              Es gibt keine Fehlermeldung, aber es kommt 2 mal "halllllloooo" auf den Bildschirm, das heist die Struktur der DB wird in der Funktion "get_dbStruct" zwei mal ermittelt.

              Doch doch, da kommt ganz bestimmt eine, nur reicht es in PHP5 nicht mehr aus, error_reporting "nur" auf E_ALL zu stellen, mann muss auch noch E_STRICT hinzufügen, damit das Hinweise bei falscher Anwendung des PHP5-Objektmodells angezeigt werden: error_reporting(E_ALL | E_STRICT) (oder numerisch 4095 (2047+2048))

              class rpDBM{
              private static $dbStruct = null;
              private static $dbRels   = null;

              public function get_dbStruct(){ // tabellen/felder
                if( is_null($this->dbStruct) ){

              Und da greifst du auf die statische Eigenschaft dynamisch zu. So gehts richtig:

              ... isnull(self::$dbStruct) ...

              echo "$verabschiedung $name";

              1. Danke, ich werde noch ein bisschen darüber grübeln.

                Die Funktion von "self" habe ich noch nicht wirklich Begriffen.

                Kannst du mir das noch erklären?

                Danke!

                1. echo $begrueszung;

                  Die Funktion von "self" habe ich noch nicht wirklich Begriffen.

                  Kannst du mir das noch erklären?

                  self ist ein Keyword, dass die aktuelle Klasse repräsentiert. Zu vergleichen ist das in etwa mit parent, mit dem man auf überladene Methoden der Elternklasse zugreifen kann.

                  self ist aber keine Ersatz oder gar das gleiche wie die magischen Konstante __CLASS__.

                  Im Kapitel Scope Resolution Operator (::) gibt es über self etwas zu lesen.

                  echo "$verabschiedung $name";

                2. Die Funktion von "self" habe ich noch nicht wirklich Begriffen.

                  self ist ähnlich zu this. Da this aber eine Referenz auf das aktuelle Objekt ist und statische Eigenschaften und Methoden jedoch nicht mit einer Instanz einer Klasse verknüpft werden können sondern "klassenglobal" für alle Instanzen einer Klasse gelten, musst Du auf statische Eigenschaften über self zugreifen.

      3. Ein Singleton ist eine Klasse, von der nur eine Instanz existieren kann:

        Und deshalb ist singleton hier für den OP nicht die richtige Herangehensweise, denn er will ja mehrere Instanzen haben, und das ist durchaus sinnvoll.

        Wenn die Klasse noch weitere Eigenschaften enthalten würde, die sich pro Instanz unterscheiden können / sollen, dann ist ein Singleton in der Tat nicht der richtige Weg. Aber so wie mir die Klasse vorgegeben wurde, war diese Angehensweise durchaus sinnvoll.

        1. echo $begrueszung;

          Ein Singleton ist eine Klasse, von der nur eine Instanz existieren kann:

          Und deshalb ist singleton hier für den OP nicht die richtige Herangehensweise, denn er will ja mehrere Instanzen haben, und das ist durchaus sinnvoll.

          Wenn die Klasse noch weitere Eigenschaften enthalten würde, die sich pro Instanz unterscheiden können / sollen, dann ist ein Singleton in der Tat nicht der richtige Weg. Aber so wie mir die Klasse vorgegeben wurde, war diese Angehensweise durchaus sinnvoll.

          Jetzt, nachdem ich das nochmal gelesen habe, denke ich auch, dass es nur eine Instanz der Klasse geben darf.
          In dem Falle würde ich mir aber die Instanziierung sparen und gleich auf eine statische Methode setzen.

            
          class DBM { // wofür auch immer dieses DB_M_ steht (Stichwort: Dokumentationskommentare!)  
            
            private static $DBStruct = array();  
            
            public static getDBStruct($tableName) {  
              if (!isset(self::$DBStruct[$tableName]))  
                self::$DBStruct[$tableName] = self::readTableStructure($tableName);  
            
              return self::$DBStruct[$tableName];  
            }  
            
            private static readTableStructure($tableName) {  
              ... // wenn Tabelle $table nicht existiert sollte eine Exception geworfen werden  
            }  
          }  
          
          

          Aufruf dann mit: $x = DBM::getStruct($tableName);

          Ob es sinnvoll ist, die DB-Struktur im Ganzen einzulesen oder nur die der jeweils benötigten Klassen (wie in meinem Beispiel), mag der OP entscheiden.

          echo "$verabschiedung $name";

          1. class DBM { // wofür auch immer dieses DB_M_ steht (Stichwort: Dokumentationskommentare!)

            DBM = Datenbank-Management

            sorry

            1. echo $begrueszung;

              class DBM { // wofür auch immer dieses DB_M_ steht (Stichwort: Dokumentationskommentare!)

              DBM = Datenbank-Management

              Hmm... "Management" ist auch nicht viel aussagekräftiger, finde ich. Ich hoffe, du schreibst ein paar erklärende Worte in den Quelltext, was die Aufgabe der einzelnen Bestandteile ist.

              Schau dir mal http://www.phpdoc.org/ an. Selbst wenn du nur für dich selbst programmierst, wirst du eine gute Dokumentation später zu schätzen wissen. Und mit diesem Stil erstellst du die Dokumentaion quasi nebenbei mit.
              Wenn das Projekt fertig ist hat man eh keine Lust mehr. Und manchmal wird das Projekt nie beendet, aber Teile daraus wandern in andere Projekte...

              Der Vorteil dieser Dokumentationsart ist nicht nur dass man hinterher eine Quelltext-Dokumentation in diversen Formaten erstellen kann, sondern auch dass einige IDEs diese Kommentare lesen und als Programmierhilfe verwenden können.

              echo "$verabschiedung $name";