Sebastian Grommes: PHP OOP BOARD

Hallo,

ich versuche ein Forum in OOP zu schrieben.

class DB {
     function connect() {
          include ("configs.php");
          $connection = mysql_connect($server,$user,$pass) or die(mysql_error());
          mysql_select_db($db,$connection) or die(mysql_error());
     }
     function close() {
          //mysql_free_result($ergebnis);
          mysql_close($connection);
     }
}

$DB = new DB();

Das soll nur die Verbindung zur Datenbank regeln und später dann so ausgeführt werden:

class Board  {
     var $Subboard = array();
     var $Themen = array();
     var $Antworten = array();

function show() {
          $DB->connect();
          $ergebnis = mysql_query("SELECT subboard,threads,answers FROM board",$connection) or die(mysql_error());
          while ($res=mysql_fetch_assoc($ergebnis)) {
               array_push($this->Subboard,$res[subboard]);
               array_push($this->Themen,$res[threads]);
               array_push($this->Antworten,$res[answers]);
          }
          foreach($this->Subboard as $var) {
               echo "Subboard: " . $var . "<br>";
          }
          foreach($this->Themen as $var) {
               echo "Themen: " . $var . "<br>";
          }
          foreach($this->Antworten as $var) {
               echo "Antworten: " . $var . "<br>";
          }
          $DB->close();
     }

Nur was bitte habe ihc falsch gemacht, wenn diese Fehlermeldung kommt:

Fatal error: Call to a member function connect() on a non-object in C:\xampp\htdocs\nedias\classes.php on line 23 ??

Kann mir da jemand bitte helfen?

Vielen Dank!

Sebastian Grommes

  1. Hello,

    $DB = new DB();
         function show() {
              $DB->connect();

    habe selbst noch nicht mit OOP unter PHP gearbeitet, aber ich vermute mal, dass du $DB schon innerhalb deiner Board-Klasse als Objekt initialisieren musst, sonst dürfte dein Methodenaufruf fehlschlagen. In deinem Codeauszug schwebt die Initialisierung im Niemandsland...

    MfG
    Rouven

    --
    -------------------
    Unser Problem ist, dass wir eine Demokratie entwickelt haben, was nicht immer der richtige Weg ist  --  Bernie Ecclestone zu den lästigen Diskussionen um Regeländerungen in der Formel 1
    1. Erstmal vielen dank, genau das wars^^

      Jetzt hab ich dann aber einen anderen Fehler:

      Warning: mysql_query(): supplied argument is not a valid MySQL-Link resource in C:\xampp\htdocs\nedias\classes.php on line 10

      Und das obwohl die Zeile 10 richtig sein müsste:

      function query($query) {
                $ergebnis=mysql_query('$query',$connection) or die(mysql_error());
           }

      Aufgerufen habe ich so:

      function show() {
                $DB = new DB();
                $DB->connect();
                $DB->query("SELECT subboard,threads,answers FROM board");

      Eigentlich kann das nicht sein. Wenn ich mir nähmlich die Variable $query ausgeben lasse erhalte ich genau SELECT subboard,threads,answers FROM board.

      Vielen Dank nochmals!

      Sebastian Grommes

      1. Hello,

        class DB {
          function connect() {
               include ("configs.php");
               $connection = mysql_connect($server,$user,$pass) or die(mysql_error());
               mysql_select_db($db,$connection) or die(mysql_error());
          }
          function close() {
               //mysql_free_result($ergebnis);
               mysql_close($connection);
          }
        }

        du musst dein $connection zu einem Attribut der DB-Klasse machen, im Moment müsste es sich wie eine lokale Variable verhalten, d.h. nach dem Aufruf von connect() ist es im Nirwana verschwunden. Wenn du beim query() darauf zugreifen möchtest ist die Ressource ungültig.

        MfG
        Rouven

        --
        -------------------
        Wenn du die Nadel im Heuhaufen nicht findest, zünde den Heuhaufen an.
        1. So,

          ich habe dann jetzt mal Singleton verwendet (ich hoffe, dass ich es richtig gemacht habe - ich kenne mich damit eigentlich überhaupt nicht so aus). Code:

          <?

          error_reporting(E_ALL);

          class DB {
               private $connection;
               static private $instance;

          static public function construct() {
                    if (!self::$instance) {
                         self::$instance = new DB();
                    }
                    return self::$instance;
               }
               public function connect() {
                    include ("configs.php");
                    $this->connection = mysql_connect($server,$user,$pass) or die(mysql_error());
                    mysql_select_db($db,$this->connection) or die(mysql_error());
               }
               public function query($query) {
                    $ergebnis = mysql_query($query,$this->connection) or die(mysql_error());
                    return $ergebnis;
               }
               public function close() {
                    mysql_close($this->connection);
               }
          }

          $DB = DB::construct();

          class Board {
               public $Subboard = array();
               public $Themen = array();
               public $Antworten = array();

          function show() {
                 $DB->connect();
                 $ergebnis = $DB->query("SELECT subboard,threads,answers FROM board");
                    while ($res=mysql_fetch_assoc($ergebnis)) {
                         array_push($this->Subboard,$res[subboard]);
                         array_push($this->Themen,$res[threads]);
                         array_push($this->Antworten,$res[answers]);
                    }
                    foreach($this->Subboard as $var) {
                         echo "Subboard: " . $var . "<br>";
                    }
                    foreach($this->Themen as $var) {
                         echo "Themen: " . $var . "<br>";
                    }
                    foreach($this->Antworten as $var) {
                         echo "Antworten: " . $var . "<br>";
                    }
                    $DB->close();
               }

          function add() {
                    $DB->connect();
                    $DB->query("INSERT INTO board (subboard,threads,answers) VALUES ('nedias','5','20')");
                    $DB->close();
               }
          }

          $Board = new Board();
          //$Board->add();
          $Board->show();

          ?>

          Aber es funktioniert nicht. Könnte mir jemand freundlicherweise sagen, warum denn Bitte immer noch gesagt wird, dass z.B. in Zeile 37 keine Instanz vorhanden ist?

          Vielen Dank!

          Sebastian Grommes

          1. echo $begrüßung;

            ich habe dann jetzt mal Singleton verwendet (ich hoffe, dass ich es richtig gemacht habe - ich kenne mich damit eigentlich überhaupt nicht so aus). Code:
            Aber es funktioniert nicht. Könnte mir jemand freundlicherweise sagen, warum denn Bitte immer noch gesagt wird, dass z.B. in Zeile 37 keine Instanz vorhanden ist?

            Du hast immer noch nicht den Gültigkeitsbereich von Variablen begriffen, wie mir scheint. Du musst Variablen in dem Bereich anlegen, in dem du sie verwenden willst[1]. Das Singleton-Muster hilft dir in dem Fall, dass es immer die selbe Instanz zurückliefert, egal wie oft du es aufrufst. In jeder Funktion/Methode, in der du die DB-Instanz benötigst, schreibst du zuerst $DB = DB::construct(); und kannst dann auf $DB zugreifen. Die Variable $DB kann dabei theoretisch in jeder Funktion anders heißen. Ihr Name tut nichts zur Sache, durch das Singleton hast du jedes Mal den selben Inhalt (den selben!, nicht nur den gleichen).

            Das sollte dein Problem zunächst lösen. Allerdings ist dein gesamtes Konzept nicht besonders geeignet, den DB-Zugriff zu kapseln. Du hast noch jede Menge mysql_*()-Funktionen in deinem restlichen Quelltext liegen. Damit mischst du den objektorientierten Ansatz wieder mit der Verwendung herkömmlicher Funktionen.

            Du könntest auch einfach mysqli in der OOP-Version einsetzen (meinetwegen auch PDO). Das lässt sich wunderbar erweitern. Du könntest beispielsweise eine eigene Klasse erstellen, die von mysqli erbt und das Singleton hinzufügt.

            Aber auch das ist noch nicht besonders übersichtlich. Ziel sollte sein, das komplette Datenbank-Handling vollständig aus der Anwendungslogik zu verbannen. Ich bin dazu übergegangen, in der Anwendungslogik nur noch einen Funktions- oder Methodenaufruf stehen zu haben, der gegebenenfalls ein paar Parameter übergeben bekommt und ein Array (oder was auch immer mir am besten geeignet scheint) als Ergebnis zurückgibt. Vom Zusammenbauen des Statements bis zum Abholen des Ergebnisses erledigt diese Funktion/Methode alles selbständig. Natürlich kann sie dabei ihrerseits auf eine DB-Klasse zugreifen. Doch das muss der eigentlichen Anwendung komplett egal sein. Selbst andere Datenquellen ((XML-)Datei, Webservice, usw.) wären so komplett transparent einbindbar.

            Ein weiterer kleiner Verbesserungsvorschlag betrifft das Inkludieren der MySQL-Verbindnungsdaten. Die Include-Datei kann zwar generell bleiben, doch würde ich darin Konstanten anlegen. (Auch andere Konfigurationswerte können bei Bedarf darin angelegt werden.) Die Datei wird einmalig (z.B. am Anfang) ins Script eingebunden. Konstanten sind generell superglobal, auf sie kann man also von überall her ohne weiteres zugreifen.

            [1] Das Keyword global lassen wir mal weg, das will ja keiner verwenden.

            echo "$verabschiedung $name";

            1. Vielen Dank für diese doch sehr ausführliche Antwort. Sie hat mir sehr geholfen.

              Sebastian Grommes

      2. Hallo,

        function show() {
                  $DB = new DB();
                  $DB->connect();
                  $DB->query("SELECT subboard,threads,answers FROM board");
        Eigentlich kann das nicht sein. Wenn ich mir nähmlich die Variable $query ausgeben lasse erhalte ich genau SELECT subboard,threads,answers FROM board.

        ja, schon möglich. Aber An die DB übergibst du nicht diesen String "SELECT ...", sondern nur den konstanten und parameterlosen String "$query", mit dem die SQL-Engine wohl nicht viel anfangen kann:

        function query($query) {
                  $ergebnis=mysql_query('$query',$connection) or die(mysql_error());
             }

        Es ist doch wieder das häufig auftretende Leiden: Warum kommen PHP-Programmierer auf die schräge Idee, eine einzelne Variable irgendwo nochmal einsam und allein in einen String einzubetten?
        Und wenn man es dann noch mit den "falschen" Anführungszeichen macht, dann hat man den Salat ...

        So long,
         Martin

        --
        Der Alptraum jedes Computers:
        "Mir war, als hätte ich gerade eine 2 gesehen."
        1. echo $begrüßung;

          function query($query) {
                    $ergebnis=mysql_query('$query',$connection) or die(mysql_error());

          Es ist doch wieder das häufig auftretende Leiden: Warum kommen PHP-Programmierer auf die schräge Idee, eine einzelne Variable irgendwo nochmal einsam und allein in einen String einzubetten?
          Und wenn man es dann noch mit den "falschen" Anführungszeichen macht, dann hat man den Salat ...

          Ja, das ist ein weiterer Fehler, der aber mit der Meldung nichts zu tun hat. Aus Sicht von mysql_query() ist der String '$query' ein gültiges Argument, wenn auch kein sinnvolles für den MySQL-Server. Der beschwert sich dann mit einer eigenen Fehlermeldung, wenn mysql_query() mit einem korrekten zweiten Parameter aufgerufen wird.

          echo "$verabschiedung $name";

  2. echo $begrüßung;

    Nur was bitte habe ihc falsch gemacht, wenn diese Fehlermeldung kommt:
    Fatal error: Call to a member function connect() on a non-object in C:\xampp\htdocs\nedias\classes.php on line 23 ??

    Dein Problem, auch das der Folgefrage, hat weniger was mit OOP zu tun als vielmehr mit dem Gültigkeitsbereich von Variablen. Innerhalb einer Funktion stehen außerhalb selbiger generierte Variablen nicht zur Verfügung. Das gilt ebenfalls für OOP. Nun könnte man mittels global-Keyword globale Variablen innerhalb der Funktion/Methode sichtbar machen, aber dafür gibt es unter OOP bessere Ansätze. Für Datenbankverbindungsobjekte bietet sich das Singleton-Pattern an.

    Gerade bei OOP solltest du strikt darauf achten, dass Objekte selbständig arbeiten können, ohne dass sie irgendwelche Variablen anderer Scopes verwenden müssen. Du erzeugst sonst wieder nur Spaghetti- statt OOP-Code.

    Die wirklichen Ursachen beider Fehler wären dir übrigens angezeigt worden, wenn du das error_reporting auf E_ALL stehen gehabt hättest. Dann hättest du eine Notice-Meldung bekommen, dass du auf nicht deklarierte Variablen zuzugreifen versuchst. E_ALL ist beim Entwickeln immer eine gute Idee.

    class Board  {
         var $Subboard = array();

    Das Schlüsselwort var ist veraltete PHP-4-Syntax. Mittlerweile steht es zwar wieder gleichberechtigt neben public, trotzdem ist es empfehlenswert, die neuen PHP-5-Sichtbarkeitsmodifizierer zu verwenden.

    echo "$verabschiedung $name";

  3. n'abend,

    class DB {
         function connect() {
              include ("configs.php");
              $connection = mysql_connect($server,$user,$pass) or die(mysql_error());
              mysql_select_db($db,$connection) or die(mysql_error());
         }
         function close() {
              //mysql_free_result($ergebnis);
              mysql_close($connection);
         }
    }

    aha. du hast ein Datenbank-interface, das aber außer Verbindung öffnen und schließen nichts weiter kann?

    $DB = new DB();

    Dein Datenbankinterface möchtest du höchstwahrscheinlich als Singleton zur Verfügung stellen. Sprich die Klasse wird nur einmal initialisiert, sprich es wird auch nur eine Verbindung zur Datenbank aufgebaut.

    class Board  {
         var $Subboard = array();
         var $Themen = array();
         var $Antworten = array();

    function show() {
              $DB->connect();

    $DB existiert nicht.

    $ergebnis = mysql_query("SELECT subboard,threads,answers FROM board",$connection) or die(mysql_error());

    Eingangs sprachst du von OOP. Das hier sieht nicht sehr OOP aus. Wieso stellst du in deiner DB-Klasse nicht eine Methode ausführen() zur Verfügung?  und Warum gibt die Methode ausführen() eine Resource-ID zurück, wenn sie doch ein neues Objekt der Klasse DBErgebnis zurückgeben könnte, in welchem du alle nötigen Zugriffe auf die Resultatmenge gekapselt hast...? (siehe unten)

    Davon mal abgesehen übergibst du der mysql_query() eine Verbindungsresource $connection - die es an dieser Stelle aber gar nicht gibt. mysql_query weiß da nicht mit welcher Verbindung es arbeiten soll.

    while ($res=mysql_fetch_assoc($ergebnis)) {
                   array_push($this->Subboard,$res[subboard]);
                   array_push($this->Themen,$res[threads]);
                   array_push($this->Antworten,$res[answers]);
              }

    array_push() kann man sich der schönheit halber auch durch $arrayname[] = $neuesElement einsparen

    Nur was bitte habe ihc falsch gemacht, wenn diese Fehlermeldung kommt:

    Fatal error: Call to a member function connect() on a non-object in C:\xampp\htdocs\nedias\classes.php on line 23 ??

    Ins deutsche Übersetzen würde meist schon helfen. Oder mal in der Zeile nachschauen, was da ggf. los sein könnte.

    class DatabaseConnection  
    {  
      private $con; // Verbindungsresource  
      
      public function __construct( $host, $user, $password, $db)  
      {  
        $this->con = mysql_connect( $host, $user, $password );  
        if( !this->con )  
          die( $this->mysql_errno() .' - '. mysql_error() );  
      
        if( !mysql_select_db( $db ) )  
          die( $this->mysql_errno($this->con) .' - '. mysql_error($this->con) );  
      }  
      
      public function exec( $sql )  
      {  
        $res = mysql_query( $sql, $this->con );  
        return new DatabaseResultSet( $this, $res );  
      }  
    }  
      
    class DatabaseResultSet  
    {  
      private $db;  
      private $res;  
      
      public function __connect( $db, $resource )  
      {  
        $this->db = $db;  
        $this->res = $resource;  
      }  
      
      public function nextRow()  
      {  
        return mysql_fetch_assoc( $this->res );  
      }  
      
    }  
      
    // Anwendung:  
    $db = new Database( 'localhost', 'local', 'horst', 'foo' );  
    $res = $db->exec( 'SELECT foo FROM bar WHERE baz = 42' );  
    while( $r = $res->nextRow() )  
    {  
      echo $r['foo'];  
    }  
    
    

    Das ist nur ein Beispiel, wie man das angehen könnte (resp. sehr vereinfacht das, was z.b. ADODB macht).

    weiterhin schönen abend...

    --
    Freundlich wie man war, hat man mir Großbuchstaben geschenkt.
    sh:( fo:# ch:# rl:| br:> n4:& ie:{ mo:} va:) de:] zu:} fl:( ss:? ls:[ js:|
  4. Moin!

    ich versuche ein Forum in OOP zu schrieben.

    Brich diesen Versuch ab. Aktualisiere deine PHP-Installation auf die neueste 5er-Version.

    Starte dann erneut, und zwar mit der in Version 5 eingeführten vernünftigen OOP-Unterstützung!

    Die PHP-Version 4 wird sich zum Jahresende 2007 selbst zerstören (hoffe ich jedenfalls - zumindest gibt es ab 2008 keinen allgemeinen Patch-Support mehr, und Security-Patches offiziell auch nur noch bis 8.8.2008 - inoffiziell allerdings wohl schon seit längerer Zeit nicht mehr).

    - Sven Rautenberg

    --
    "Love your nation - respect the others."