René Marscheider: class FOO extends BAR

Hi,

zwei Klassen wie kann ich von der Kind-Klasse auf die Vater-Klasse zugreifen. Ich erhalte bei meinen Versuchen diese Fehlermeldung:

-----
Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in E:\projekt\page\classen\db_class.php on line 49
-----

class datenbank
{
 var $dbhost;
 var $dbuser;
 var $dbpass;
 var $dbname;
 var $conn;

function db_error($text)
 {
  $num = mysql_errno();
  $msg = mysql_error();

require($_SERVER['DOCUMENT_ROOT'] . '/classen/db_error.php');

echo $error_text;
  exit();
 }

function db_conn()
 {
  $this->conn = @mysql_connect($this->dbhost, $this->dbuser, $this->dbpass);
  if(!$this->conn) $this->db_error('Verbindungsfehler');

$this->sele = @mysql_select_db($this->dbname, $this->conn);
  if(!$this->sele) $this->db_error('Datenbankfehler');

return $this->conn;
 }

function db_query($query)
 {
  $this->result = @mysql_query($query, $this->conn);
  if(!$this->result) $this->db_error('Queryfehler');

return $this->result;
 }

function db_unbuffered_query($query)
 {
  $this->result = @mysql_unbuffered_query($query, $this->conn);
  return $this->result;
 }

function db_fetch_row($result)
 {
  $this->fetch_row = mysql_fetch_row($result);
  return $this->fetch_row;
 }

......

}

$db = new datenbank;

$db->dbhost = DB_HOST;
$db->dbuser = DB_USER;
$db->dbpass = DB_PASS;
$db->dbname = DB_NAME;

$db->db_conn();

/**********/

class user_delete extends datenbank
{
 var $zeit  = 0;
 var $user  = 0;
 var $dtemp = array();

function user()
 {
  $delete = 'SELECT id, reg\_time, activ FROM user';
  $result = parent::db_unbuffered_query($delete);

while($rows = parent::db_fetch_row($result))
  {
   if(($rows[1] + ($this->user * 86400)) < $this->zeit && $rows[2] == 0)
   {
    array_push($this->dtemp, $rows[0]);
   }
  }

if(sizeof($this->dtemp) > 0)
  {
   $del_query = 'DELETE FROM user WHERE id IN (' . implode(',', $this->dtemp) . ')';
   $opt_query = 'OPTIMIZE TABLE user';
   parent::db_query($del_query);
   parent::db_query($opt_query);
  }
 }
}

$del_user = new user_delete;

$del_user->zeit = $zeit;
$del_user->user = $del_user;

$del_user->user();

  1. Hi,

    zwei Klassen wie kann ich von der Kind-Klasse auf die Vater-Klasse zugreifen. Ich erhalte bei meinen Versuchen diese Fehlermeldung:


    Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in E:\projekt\page\classen\db_class.php on line 49

    du greifst schon richtig mittels "parent" auf die Methode der Basisklasse zu. Die Fehlermeldung spricht eher dafür, dass du einen Fehler in dem SQL-Statement eingebaut hast. Zur Prüfung solltest du dir mal eben dieses Statement ausgeben lassen, z.B. im Falle eines Fehlers in der Methode db_unbuffered_query:

    function db_unbuffered_query($query)
     {
      $this->result = @mysql_unbuffered_query($query, $this->conn)
          or die('Fehler: ' . mysql_error() . '<br />SQL: ' . $query);
      return $this->result;
     }

    Nun musst du schauen, was für eine Fehlermeldung ausgegeben wird und dahingehend das SQL-Statement prüfen.

    Gruß,
    Andreas.

    1. Hi Andreas,

      Nun musst du schauen, was für eine Fehlermeldung ausgegeben wird und dahingehend das SQL-Statement prüfen.

      wenn ich die Klasse "class user_delete ...." als normalen Code in die Seite einfüge also nicht als Klasse dann funktioniert es.

      -----
        $delete = 'SELECT id, reg\_time, activ FROM user';
        $result = parent::db_unbuffered_query($delete);

      while($rows = parent::db_fetch_row($result))
      ----

      Das Problem ist das das "parent::db_unbuff...()" nichts mehr aus der Klasse datenbank zurückliefert. Somit ist $result leer und db_fetch_row gibt den Fehler aus.

      1. Hi,

        Das Problem ist das das "parent::db_unbuff...()" nichts mehr aus der Klasse datenbank zurückliefert. Somit ist $result leer und db_fetch_row gibt den Fehler aus.

        ja - ich sehe schon, _ich_ habe die Fehlermeldung missverstanden ;-) Ich vermute, das Problem ist, dass du in der Instanz $del_user keine Verbindungskennung hast (also $this->conn in db_unbuffered_query() nicht existiert), da du db_conn() für diese Instanz nicht aufgerufen hast.

        Gruß,
        Andreas.

        1. Hi Andreas,

          dass du in der Instanz $del_user keine Verbindungskennung hast (also $this->conn in db_unbuffered_query() nicht existiert)...

          wie bekomme ich das $db_conn in die Funktion, die beiden Funktionen also das db_connect welches das $conn liefert und unbuff...() stehen doch in der Klasse datenbank??

          Gruß
          René

    2. Moin!

      du greifst schon richtig mittels "parent" auf die Methode der Basisklasse zu.

      Ja? Würde ich genau _nicht_ so sehen.

      Sinn einer "extends"-Klassenerweiterung ist ja, dass die neue Klasse alle Methoden der alten Klasse erbt und benutzen kann. Ein Aufruf der Methode der Elternklasse ist also in 99% aller Fälle überflüssig, weil man dieselbe Methode auch in der eigenen Klasse hat. Oder sie dort so verändert hat, wie man sie gerade brauchte.

      Außerdem ist der statische Aufruf parent::methode() an ganz andere Bedingungen geknüpft. Statische Aufrufe sind vergleichbar mit dem simplen Aufrufen einer definierten Funktion - nur dass die Funktion eben in einer Klasse steht. Man hat aber keinerlei Klassenvariablen zur Verfügung ($this->variable), der Konstruktor wird nicht aufgerufen, und es gibt eben keine mehreren Instanzen.

      Insofern halte ich diesen statischen Aufruf an dieser Stelle für falsch - und auch generell nicht für sehr glücklich, wenn man mit Instanzen arbeitet.

      Also statt dem statischen Aufruf parent:: lieber $this->methode() nehmen. Weil diese Methode tatsächlich auch in der erweiterten Klasse existiert.

      Die aktuelle Version der Datenbankklasse sehe ich etwas kritisch. Man sollte vielleicht im Konstruktor (dem man bei der Instanziierung der Klasse Parameter mitgeben kann) die Definition der Passworte und das Kontakten der DB regeln - zumindest aber sollte es für sowas eine eigene Methode geben. Der Direktzugriff auf Eigenschaften einer Klasse wird von den Gurus als böse angesehen, man soll get/set-Methoden für sowas verwenden, weil man dadurch eine Kapselung und definierte Schnittstelle erreicht, die unabhängig von der internen Datenspeicherung ist.

      In Bezug auf [pref:t=77839&m=450125]:

      Den Konstruktor der übergeordneten Klasse muß man im Konstruktor der erbenden Klasse manuell selbst aufrufen.

      - Sven Rautenberg

      1. Hallo Sven,

        du greifst schon richtig mittels "parent" auf die Methode der Basisklasse zu.

        Ja? Würde ich genau _nicht_ so sehen.

        Falsch ist es nicht, aber in dem Fall unlogisch.

        Außerdem ist der statische Aufruf parent::methode() an ganz andere Bedingungen geknüpft.
        Statische Aufrufe sind vergleichbar mit dem simplen Aufrufen einer definierten
        Funktion - nur dass die Funktion eben in einer Klasse steht. Man hat aber keinerlei
        Klassenvariablen zur Verfügung ($this->variable), der Konstruktor wird nicht aufgerufen,
        und es gibt eben keine mehreren Instanzen.

        Quatsch. Statische Aufrufe sind zwar dementsprechend gekennzeichnet, aber parent::method()
        ist kein statischer Aufruf. Nachvollziehbar an einem ganz einfachen Beispiel:

        ckruse@srv001:~ $ cat test.php
        <?php

        class A {
          var $blub = "a";

        function x() {
            echo "A: ".$this->blub."\n";
          }
        }

        class B extends A {
          var $blub = "b";
          function x() {
            parent::x();
            echo "B: ".$this->blub."\n";
          }
        }

        $x = new B();
        $x->x();

        ?>
        ckruse@srv001:~ $ php test.php
        A: b
        B: b
        ckruse@srv001:~ $

        Grüße,
         CK

        --
        Keine Schneeflocke faellt je auf die falsche Stelle.
        1. Hi,

          aber wie muss es jetzt richtig sein auch wenn ich stat parent:: $this verwende funktioniert es mit dem $conn nicht?

          Gruß
          René

          1. Moin!

            Hi,

            aber wie muss es jetzt richtig sein auch wenn ich stat parent:: $this verwende funktioniert es mit dem $conn nicht?

            Was logisch ist.

            Du hast eine Instanz der Klasse "datenbank" in der Variablen $db. Mit der Instanz stellst du die DB-Verbindung her.

            Dann hast du - davon vollkommen unabhängig - eine Instanz der Klasse "user_delete" in der Variablen $del_user, mit der du eine Löschoperation durchführen willst.

            Dein Problem ist nun: Woher weiß die Instanz in $del_user, dass die Instanz in $db eine Datenbankverbindung hergestellt hat? Du sagst es ihr nicht? Du übergibst keinen Verweis/Pointer/Methode/sonstwas.

            Würdest du dagegen einfach nur eine Instanz von "user_delete" erzeugen, dort die Datenbankverbindung herstellen, und dann löschen (indem du mit $this arbeitest, um die allgemeine DB-Methode aufzurufen), dann würde es gehen.

            Deine Klassenstruktur ist aber sowieso etwas fragwürdig. Warum trennst du DB-Klasse und User-Löschen-Klasse als Erweiterung der DB-Klasse? Ich würde das alles in eine Hauptklasse packen. Denn wie wahrscheinlich ist es, dass du zwei verschiedene User-Löschen-Funktionen benötigst - und das auch noch unter _demselben_ Namen?

            Da du im Endeffekt die Vererbung der Klassen nur als mäßigen Include-Ersatz benutzt, kannst du auf die Vererbung eigentlich auch genausogut verzichten - IMHO.

            - Sven Rautenberg

  2. Hallo!

    zwei Klassen wie kann ich von der Kind-Klasse auf die Vater-Klasse zugreifen. Ich erhalte bei meinen Versuchen diese Fehlermeldung:

    Ich würde hier allerdings nicht mit Vererbung arbeiten. Du kannst zwar mit Vererbung arbeiten, ist vielleicht einfacher, aber wenn Du nur eine Db-Verbindung brauchst, würde ich ein Singleton-Pattern einsetzen wollen, da Du so eine globale DB-Variable vermeidest.

    Erstmal zur Vererbung:

    $db = new datenbank;

    $db->dbhost = DB_HOST;
    $db->dbuser = DB_USER;
    $db->dbpass = DB_PASS;
    $db->dbname = DB_NAME;

    $db->db_conn();

    das würde ich schonmal nicht machen.

    Man sollte nach Möglichkeit nicht auf Variablen in einer Klasse direkt von außen zugreifen, weil das eine Kapselung zunichte machen kann.

    Eher sowas:

    $db = new datenbank(DB_HOST,DB_USER,DB_PASS,DB_NAME);

    dann brauchst Du in der DB-Klasse eine Konstruktor-Funktion mit dem Namen "datenbank", die die Parameter entsprechend weiterverarbeiten und damit z.B. $this->db_conn($host,$user,$pass,$name); aufruft. Dadurch wird deine interne Implementierung vollkommen unabhängig vom Rest der Applikation.

    Jetzt hast Du in $db ein Objekt mit einer DB-Verbindung, was Du überall einsetzen kannst, leider eine globale Variable.

    Wenn Du hier wirklich mit Vererbung arbeiten willst, musst Du nicht " new datenbank" verwenden, sondern direkt die erbende Klasse aufrufen. Aber das finde ich schlecht weil dann möglicherweise andauernd neue Verbindungen aufgebaut werden.

    Zum Thema Singleton-Pattern:

    Die Idee dahinter ist, dass Du eine Methode/Funktion hast, die selber genau eine(!) Instanz der DB-Klasse in einer statischen Variable hält.

    class DB {
      var $conn;

    function DB() {
        $this->connect();
      }

    function connect() {
        // Verbindung herstellen
      }

    function query($sql) {
        // SQL an DB senden
      }

    function & getConnection() {
        // statische Variable die eine DB-Objekt Instanz hält
        static $instance;

    // Wenn noch kein Objekt existiert, erzeuge eins
        if(!is_object($instance)) {
            $instance = new DB;
        }
        return($instance);
      }
    }

    Jetzt kannst Du überall in der Applikation ohne globale Variablen zu verwenden diese eine DB-Verbindung nutzen

    Z.B. so
    $db1 =& DB::getConnection();
    $db1->query('SELECT...');

    an einer anderen Stelle(in einer anderen Klasse...) z.B. so:

    class User {

    var $db;
      var $uid;
      var $name;

    function User() {
        $this->db =& DB::getConnection();
      }

    function getUser($id) {
        $res = $this->db->query("SELECT name FORM users WHERE uid = '".$id."'");
        $row = $res->fetch_row();
        $this->uid = $id;
        $this->name = $row['name'];
      }

    function storeChanges() {
        $this->db->query("INSERT INTO users (name) VALUES ('".$this->name."')");
      }

    function setName($new) {
        $this->name = $new;
      }

    function getName() {
        return $this->name;
      }

    function delUser() {
        $this->db->query("DELETE FORM users WHERE uid = '".$this->uid."'");
      }
    }

    $user =& new User();
    $user->getUser(123);// evtl kann man das auch direkt über den Konstruktor machen
    echo $user->getName();
    $user->setName('Hannes');
    $user->storeChanges();
    $user->delUser();

    So in etwas würde ich das heute glaube ich machen. Nur mit dem Unterschied dass ich den SQL-Kram noch aus der User-Klasse eliminieren würde, und zwar in eine eigene DAO-Klasse, für jedes Objekt der "Business-Logik" .

    Du kannst natürlich die getConnection() Funktion anpassen, dass man der auch DB-Zugangsdaten übergeben kann, Betonung auf _kann_, so kann man diese Funktion am Anfang einmal aufrufen mit den Zugangsdaten, und dann so verwenden wie von mir beschrieben. Oder Du fragst die Zugangsdaten aus der Klasse heraus ab, da weiß ich im Augenblick selber nicht so genau wie ich das machen würde.

    Was meinst Du / was meint Ihr dazu?

    Viele Grüße
    Andreas

    1. Hallo Andreas,
      das geschriebene muss ich erstmal verdauen, so fit bin ich da wohl doch noch nicht drin.

      1. Hallo René,

        das geschriebene muss ich erstmal verdauen, so fit bin ich da wohl doch noch nicht drin.

        Och - frag nach wenn Dir was nicht klar ist. Der von mir beschriebene Weg ist nur einer von vielen die möglich sind. Das Problem an "guter OOP" ist, dass es sich im Prinzip erst in großen Projekten wirklich bewährt. Das heißt zum einen, dass es bei kleineren (normalen) PHP-Projekten nicht soo wichtig ist. Auf der anderen Seite ist es sehr schwer einige Dinge in der Theorie zu verstehen und nachvollziehen zu können, weil man die Vorteile eben oft erst in großen Projekten wirklich selber sieht. Vorher kommt einem das ganze manchmal recht umständlich und unnötig vor... ;-)

        Grüße
        Andreas