Programmieren mit Klassen -> Mysql Klasse

aargau

Erfahrenes Mitglied
Ich habe dieses Wochenende begonnen mit Klassen zu Programmieren. Bis jetzt habe ich nur mit Funktionen gearbeitet. Dies macht das ganze aber oft sehr unübersichtlich und wirklich wiederverwendbar sind die Funktionen auch nicht.
Zum einstieg habe ich begonnen mein User Management in eine Klasse zu schreiben. Die Methode Login Funktioniet auch schon einwandfrei, dennoch ergeben sich für mich ein paar Fragen welche für mich nocn nicht ganz klar sind.

Es geht mir aber vorallem um meine DB Klasse. Ich möchte alles so Performant wie möglich Programmieren da gewisse Projekte von mir doch schon einige User haben. Nun meine Frage dazu:

PHP:
class DB{
    private $connection= NULL ; 
    private $query =0  ; 
    private $error = NULL ; 
    private $host= NULL; 
    private $database = NULL ; 
    private $user= NULL;
    private $pass = NULL ; 
    
    public function __construct($host=NULL, $database=NULL, $user=NULL, $pass=NULL)
    {
        $this->host = $host ; 
        $this->database = $database ; 
        $this->user = $user ; 
        $this->pass = $pass ; 
    }
    
    private function connect()
    {
        if(!$this->connection)
        {
    	  $this->connection = mysql_connect($this->host,$this->user,$this->pass,TRUE);
       	  mysql_select_db($this->database, $this->connection);            
        }
        
    }
    
     public function query($query) {
        $this->connect() ; 
      	$this->result=mysql_query($query,$this->connection);
      	$this->query++ ; 
      }
     
      public function back() {
      	return mysql_fetch_assoc($this->result);
      }
     
     
      public function count() {
      	if($this->counter===NULL && is_resource($this->result)===true) {
      		$this->counter=mysql_num_rows($this->result);
      	}
     
    	return $this->counter;
      }
}

1. Ist es sinnvoll auf jeder Seite
PHP:
$db = new DB ;
zu machen und in den anderen Klassen via GLOBAL zuzugreiffen oder sollte ich (da die Datenbank ja eigentlich nur in anderen Klassen verwendet wird diese immer erst da mittels
PHP:
$db = new DB ;
laden? Ich habe irgend wie das gefühl das per GLOBAL weniger RAM verwendet wird da sie ja nur einmal geladen wird.

2. Mach ich nur ein Denkfehler oder ist diese Klasse unter umständen sogar eine Sicherheitslüclke?
Ich gehe nun davon aus:
Ich brauche die Benutzerdaten und hole diese:
PHP:
$db->query("SELECT email, nickname FROM benutzer WHERE id='999'") ;
Danach werden die Resultate ausgegeben...
PHP:
print_r($db->back()) ;
ich starte eine zweite query welche irgend welche andere Daten holen soll, aber aus welchem grund auch immer fehlschlägt. Nun sollte ja per
PHP:
print_r($db->back()) ;
immer noch das alte Resultat zu sehen sein, oder mache ich da nur einen Denkfehler?
Falls ja, was gibt es für alternative Lösungen?

Die Klasse ist noch nicht 100% ferig, es soll noch ein error logging hinzugefügt werden etc.
 
Ich nehme normalerweise nicht mal globals, sondern greiffe direkt auf $GLOBALS zu
PHP:
$GLOBALS['db']->query($sql);

zum Zweiten:
Ich sehe das anderst als du. Nach einem Misslungenen query sollte nicht der alte Wert zurückkommen. Um das sicherzustellen musst du in $db->query() prüfen ob kein Fehler aufgetaucht ist. Wenn ja, dann $this->result mit NULL überschreiben.
 
Es gibt da ein schönes Entwurfsmuster namens lazy loading. Dabei wird in diesem Fall nur dann eine Datenbankverbindung aufgebaut, wenn diese auch verwendet wird. Das ist möglich, wenn du das mit einem Singleton kombinierst.
PHP:
class Database
{
  private static $_instance = null;
  private $_hostname = null;
  private $_database = null;
  private $_username = null;
  private $_password = null;
  
  private function __construct($hostname, $database, $username, $password)
  {
    $this->_hostname = $hostname;
    $this->_database = $database;
    $this->_username = $username;
    $this->_password = $password;
  }
  
  public static function getInstance($hostname, $database, $username, $password = null)
  {
    if(self::$_instance === null) {
      self::$_instance = new self($hostname, $database, $username, $password);
    }
    
    return self::$_instance;
  }
  
  # ... weitere Methoden
}


# Aufruf
$database = Database::getInstance('127.0.0.1', 'database_one', 'user_one', '****');
$database->query('SELECT * FROM `table`');
 
Das die Verbindung nur hergestellt wird wenn sie auch gebraucht wird (was sie aber wohl eh auf jeder Seite wird, ausser vieleicht bei Gästen, aber selbst da dürfte für die Statistiken mind. ein Insert geschehen) habe ich mit connect() versucht zu Lösen. Somit sollte erst beim ersten $db->query() aufruf die Verbindung aufgebaut werden.

@Yaslaw: Danke für den $GLOBALS[] wusste ich gar nicht, danke für den Hinweis. Zum weiten: Stimmt, die Variable wird ja bei einem $db->query() so oder so überschriben, somit sollten keine anderen Daten mehr vorhanden sein. Werde aber noch überhrüfen ob die Anfrage erfolgreich war und sonst NULL zurückgeben.


Ich denke euch jedenfalls schon mal für die Hilfe, vieleicht kommen noch ein paar Fragen dazu im verlaufe meiner Tests.
 
Du könntest auch noch ein Query-Caching verbauen, in dem du die letzte Abfrage als Zeichenkette speicherst und, falls du noch einmal eine ähnliche Abfrage absetzen, deren Daten dann nutzt um beispielsweise die Datensatzanzahl mit PHP ermittelst oder einfach nur eine Untermenge an Datensätzen daraus abfragst oder ähnliches.
 
Ich habe nun ein Problem. In der User Management Klasse habe ich zwei - drei Abfragen welche gemacht weden fürs Login. Intereessanterweise läuft immer die erste query nicht, heisst die Verbindung steht nicht. Wie man oben aber sieht checke ich ja bei jedem query() ob eine Verbindung steht oder stelle sie sonst vor der Abfrage her. Könnt ihr mir sagen was da falsch läuft?
Setze ich ein $db->connect() vor der 1. abfrage hin (connect() habe ich testweise als public gesetz) Funktioniet es einwandfrei.
 
Gut, wiso habe ich nun herausgefunden (man sollte die Fehlermeldungen etwas genauer lesen...)
PHP:
Warning: mysql_real_escape_string() [function.mysql-real-escape-string]: Access denied for user 'ODBC'@'localhost' (using password: NO) in D:\USER\htdocs\cl\error.php on line 48
mysql_real_escape_string() verursacht das Problem weil dies ja noch vor dem connect ausgeführt wird...
Gibt es dazu eine Lösung?

EDIT:
Ich habe eine Lösung gefunden...
Einfach die Methode escape() eingebaut und einfach direkt da eine Verbindung herstellen. Verbesserungsvorschläge an der Klasse sind immer willkommen, wie gesagt vorallem im bezug auf Performance und Speicherverbrauch.

PHP:
/*
Copyright by aargau 2011
*/
class DB
{

    private $connection = null;
    public $query = 0;
    private $error = null;
    private $host = null;
    private $database = null;
    private $user = null;
    private $pass = null;

    public function __construct($host = null, $database = null, $user = null, $pass = null)
    {
        $this->host = $host;
        $this->database = $database;
        $this->user = $user;
        $this->pass = $pass;
    }

    private function connect()
    {
        if (!$this->connection) 
        {
            $this->connection = mysql_connect($this->host, $this->user, $this->pass, true);
            mysql_select_db($this->database, $this->connection);
        }

    }

    public function query($q)
    {
        $this->connect();
        $this->result = mysql_query($q, $this->connection);
        $this->query++;
    }

    public function back()
    {
        return mysql_fetch_assoc($this->result);
    }


    public function count()
    {
        $this->counter = mysql_num_rows($this->result);
        return $this->counter;
    }

    public function escape($string)
    {
        $this->connect();
        return mysql_real_escape_string($string);
    }
}
 
Zuletzt bearbeitet:
Zurück