Elegantes Nutzen einer DB Klasse in PHP

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

  • Elegantes Nutzen einer DB Klasse in PHP

    Hallo zusammen,

    ich hab mal paar Fragen, die mir keine Ruhe geben. Wäre nett, wenn ihr ein paar Kommentare einwerft.

    Szenario:
    ich hab meine DB Klasse:

    PHP-Code:
    class NiceDB
    {
        
    //Connection info - könnte man auch über den konstruktor setzen...
        
    var $DB_Host "localhost";
        var 
    $DB_User "root";
        var 
    $DB_Password ="";
        var 
    $DB_Database="dbtest";
        
        var 
    $DB_Connection;
        
        
    //extra stuff
        
    var $LogErrors 1// 0 do not log / log
        
    var $Logfilename "DB_errorLog.log"

        
    /**
         * creates DB Connection with default parameters
         * @access public
         * @param string $newConnection opens a new Con! No matter if there is already a Con open with these params
         */
        
    function NiceDB($newConnection =0)
        {
            
    $this->DB_Connection=mysql_connect($this->DB_Host$this->DB_User$this->DB_Password$newConnection) or die(mysql_error());
            
    mysql_select_db($this->DB_Database$this->DB_Connection) or die ("Datenbankverbindung zu $this->DB_Database kann nicht hergestellt werden!");
        }
        
        
    /**
         * changes used Database
         * @access public
         * @param string $database Databasename
         */
        
    function changeDatabase($database)
        {
            
    mysql_select_db($database$this->DB_Connection) or die ("Datenbankverbindung zu $database kann nicht hergestellt werden!");
        }
        
        
    /**
         * logger
         * @access private
         * @param string $entry eintrag, der geloggt werden soll 
         */
        
    function writeLog ($entry)
        {
            if (
    $this->LogErrors && $this->Logfilename)
            {
                
    $logfile fopen($this->Logfilename'a');
                
    fwrite($logfile,"\n[".date('m.d.y H:i:s')."]\n".$entry);
                
    fclose($logfile);
            }
        }
        
        
    /**
         * insert a row into db
         * @access public
         * @param string $tablename Tabellenname 
         * @param object $object Ojekt, das eingefügt werden soll
         * @param boolean $echo true -> soll sql-statement ausgegeben werden?
         * @return 0 - if fails, PrimaryKeyValue if successful
         */
        
    function insertRow ($tablename$object$echo=0)
        {        
            
    $arr=get_object_vars($object);
            if (!
    is_array($arr))
            {
                
    $this->writeLog("insertRow: Objekt enthält nichts! - Tablename:".$tablename);
                return 
    0;
            }
            
            
    $columns="(";
            
    $values="(";
            
    $keys=array_keys($arr);
            for (
    $i=0;$i<count($keys);$i++)
            {
                if (
    $i==count($keys)-1)
                {
                    
    $columns.=$keys[$i].") values";
                    if (
    $object->$keys[$i]=="now()"//nicht sauber, aber nützlich
                        
    $values.=$object->$keys[$i].")";
                    else
                        
    $values.="\"".mysql_real_escape_string($object->$keys[$i])."\")";
                }
                else 
                {
                    
    $columns.=$keys[$i].", ";
                    if (
    $object->$keys[$i]=="now()"//nicht sauber, aber nützlich
                        
    $values.=$object->$keys[$i].", ";
                    else 
                        
    $values.="\"".mysql_real_escape_string($object->$keys[$i])."\", ";
                }
            }
            
            
    $stmt="insert into $tablename $columns $values";
            if (
    $echo)
                echo(
    $stmt);
            
    $res=mysql_query($stmt,$this->DB_Connection);
            if(!
    $res
            {
                
    $this->writeLog($stmt."\n".mysql_errno() . ": " mysql_error());
                return 
    null;
            }
            else 
            {
                
    $erg1=mysql_query('select LAST_INSERT_ID()',$this->DB_Connection);
                if(
    $erg1)
                    if (
    $res1=mysql_fetch_row($erg1))
                        if (
    $res1[0]>0)
                            return 
    $res1[0];
                        else
                            return 
    1// then this is non auto_increment - but insert was successful
            
    }
        }
    //... usw.

    und ich hab eine Kunde Klasse:
    PHP-Code:
    class Kunde
    {
        var 
    $cUserName;
        var 
    $cPassword;
        var 
    $cAnrede;
        var 
    $cTitel;
        var 
    $cName;
        var 
    $dDatetime;
        
    //funktionen usw..


    hier kommt das ausführende script:
    PHP-Code:
    require_once ("NiceDB.php");
    require_once (
    "Kunde.php");

    $DB = new NiceDB();
    $Kunde = new Kunde();

    $Kunde->cUserName="Crysus";
    $Kunde->cPassword="pass";
    $Kunde->cAnrede="H";
    $Kunde->cTitel="Dr.";
    $Kunde->cName="1\2\\3\\\4\\\\5\\\\\_1'_2''_3'''_4''''";
    $Kunde->dDatetime="now()";

    //insert in DB
    $key $DB->insertRow("test",$Kunde); 
    was mich nun daran stört:

    mein $DB Objekt muss instanziiert und vorhanden sein, um DB Queries abzusetzen. Es muss global sein, um es von überall nutzen zu können - wenn es nicht gloabl sein sollte, kann man $newDB = new NiceDB() aufrufen und man erhält die offene, in diesem Script bereits existierende Verbindungkennung. Brauche ich mehrere DB Verbindungen, um verschachtelte Queries anzusetzen, erstelle ich mir soviele neue wie ich brauche mit dem gesetzten newConnection param. ABER: Globale Objekte passen nicht so ganz in mein OOP Bild

    Wenn ich nun aber jeder Klasse, die etwas in der DB schreiben oder lesen will, eine NiceDB Klasse einniste und sie im Konstruktor instanziiere, dann muss ich mich um die $DB verbindung nicht mehr kümmern, jedes Objekt, das die DB braucht wird es enthalten - ich muss mich nicht darum scheren, ob eine Verbindung gloabal offen ist oder nicht. Was mir dabei nicht gefällt ist, dass jedes Objekt zusätzlich Speicher und Ressourcen verbrät. Mehrere Verbindungen zur DB sind nicht mehr so einfach möglich - liese sich aber noch über den Kontruktor regeln.

    Möchte man Ressourcen sparen, implementiert man die DB Klasse als Singleton, dann kann man die mehrfachen Verbindungen zur DB definitiv vergessen.

    Was ist der beste Weg?

    2. Frage:
    Da ich normalerweise schreibfaul bin, erstelle ich für jede meiner Tabellen eine Klasse mit denselben Varnamen und -anzahl wie in der jeweiligen Tabelle. Damit spare ich mir ne Menge Schreibarbeit für die einfachen select / inserts / update statements, indem ich jeweils eine Standardfunktion (wie die insertRow im Bsp. oben) für _alle_ einfachen Queries benutze. Ändert sich eine Tabelle, mache ich dieselbe Änderung in der jeweligen Klasse und alle SQL-Queries werden kompatibel sein. Kombiniert man das mit PHPUnit tests, ist diese Methode die effizienteste in meinen Augen. Einwände?

    Natürlich kommt man bei komplexeren Queries wie joins nicht drumherum, diese selber zu schreiben.

    3. Frage: Sieht jmd in der insertRow Funktion eine Möglichkeit SQL zu injecten?


    danke euch,
    Cry

  • #2
    Moin,

    zu 1.
    Du kannst ein Singleton so umprogrammieren, dass auch mehrere definierte Instanzen erlaubt sind.
    Aber bei deiner jetzigen Klasse erkenne ich nicht, dass es ein Singleton ist.
    Zumal die Umsetzung in PHP4 noch nicht möglich ist.

    zu 2.
    Das ist eine schlechte Lösung, da du immer wieder neue Klassen schreiben musst.
    Ich habe ein allgemeines Datenobjekt, welches ich über eine spezielle DB Methode speichern/updaten/auslesen kann. Ich muss nur Membervariablen per unset aufheben, welche nicht gespeichert werden sollen.

    Der nächste Schritt wäre, dass die DB Methode das auch selber erkennt.

    Gruß Thomas

    Kommentar


    • #3
      Moin,

      danke für den Kommentar.

      Ich muss schon mit php4 arbeiten, da der code in möglichst vielen Umgebungen laufen muss - auch auf älteren.

      zu2:
      Naja, die Klassen muss ich ja so oder so schreiben, wenn ich mit dem Inhalt der db tables einigermaßen vernünftig arbeiten möche - dazu kann ich mir diese Rohklassen automatisch generieren lassen, indem ich die DB tables abklappere.

      Deine Methode klingt nicht schlecht, du musst aber die objekte vor dem inserten / updaten klonen, den geklonten dann die vars unsetten und dann in die DB hauen - bei größeren Objektmengen und Tablegrößen nicht allzu sauber.

      Die DB Methode es selber erkennen lassen ist da schon die bessere Idee, jedoch braucht man dann für jeden einfachen Query einen Vorquery, der die Tabellenbeschreibung holt. Auch unnötiger Overhead in meinen Augen.

      Grüße
      Crysus

      Kommentar


      • #4
        Vernünftig arbeiten kannst du auch ohne Klassen aus deiner Hauptklasse abzuleiten (falls du das überhaupt mit ableiten machst, wäre leichter weil du die Klasse nicht komplett neu schreibst, sondern nur deine Methoden überschreiben musst, wobei nur deine insert_row Methode geändert werden müsste, oder??)

        Meine Objekte muss ich nicht Klonen. Ich lese mein Query aus der DB aus und lass mir aus dem Array mein Objekt erzeugen. Wenn ich den Inhalt meines Objekts speichern will, übergebe ich es meiner DB Methode und die bastelt daraus den update/insert String und speichert dann.
        Ich habe nur 1 Datenbank Objekt (richtiges Singleton ) und 1 Datenobjekt pro Ergebniss.
        Ich speichere meine Objekte erst, wenn ich sie nicht mehr brauche (zwischendurch macht auch nicht viel Sinn?!) und dann kann ich auch Variablen entfernen, die nicht in der DB benötigt werden.

        Die Vorüberprüfung würde wirklich die DB etwas mehr belasten, aber das würde sich in Grenzen halten. Zumal ich momentan auch nur zwei Werte aus dem Objekten löschen muss, 'time' und 'date', weil ich diese automatisch erzeugen lasse, wenn sie nicht existieren.

        Kommentar

        Lädt...
        X