Lupus: Klassen-Variablen in externen Funktionen

Hallo Forum,

Ich habe eine Klasse geschrieben die mit einer MySQL Datenbank kommuniziert. Nun habe ich eine Variable erstellt (die als Variable der Klasse erzeugt wird) die definiert ist als Präfix und dann den Variablenamen: ($db->user = $table_prefix . 'users'). Das funktioniert soweit auch ganz gut, wenn die Variable nicht in einer anderen Funktion (ich meine nicht einer Methode der Klasse) abgerufen wird.
Wie kann ich $db->user trotzdem in der Funktion gebrauchen?

Hier mein stark vereinfachter Code:

  
  /*  
   * includes.php:  
  */  
  
  include 'functions.php';  
  include 'config.php'; // in der $table_prefix und die DB_[...] Konstanten sind  
  
  $db->user  = $table_prefix . 'users';  
  
  function login_right($username, $pwd) {  
      $username = strtolower($username);  
      $sql = "SELECT COUNT(*) as Anzahl FROM $db->user  
                  WHERE  
                      username = LOWER('".$username."') AND  
                      password = MD5('".$pwd."');"; //  in dieser Abfrage ist $db->user  
      $checklogin = new db($sql);  
      $row = $checklogin->fetch();  
      return $row['Anzahl'];  
    }  
  
  login_right("Lupus", "mypassword");  
  // Fehlermeldungen:  
  // - Undefined variable: db...  
  // - Trying to get property of non-object...  
  
  
  
  
  /*  
   * functions.php:  
  */  
  
  class db {  
      var $_sql = "";  
      var $_result = 0;  
      var $_error = "";  
      // hier public user = ""; geht auch nicht...  
  
      function db($var) {  
          $this->_sql = trim($sql);  
          $this->_result = mysql_query($this->_sql);  
          if(!$this->_result) {  
              $this->_error = mysql_error();  
              $this->error();  
          }  
      }  
  
      function open() {  
          @mysql_connect(DB_HOST, DB_USER, DB_PASS) OR die(mysql_error());  
          mysql_select_db(DB_DATABASE) OR die(mysql_error());  
      }  
  
      function error() {  
          die($this->_error);  
      }  
  
      function fetch() {  
          return mysql_fetch_assoc($this->_result);  
      }  
  }  

Vielen Danke für Hilfe,
Lupus

  1. Moin!

    Ich habe eine Klasse geschrieben die mit einer MySQL Datenbank kommuniziert. Nun habe ich eine Variable erstellt (die als Variable der Klasse erzeugt wird) die definiert ist als Präfix und dann den Variablenamen: ($db->user = $table_prefix . 'users'). Das funktioniert soweit auch ganz gut, wenn die Variable nicht in einer anderen Funktion (ich meine nicht einer Methode der Klasse) abgerufen wird.
    Wie kann ich $db->user trotzdem in der Funktion gebrauchen?

    Du solltest:
    1. Deine Datenbankklasse als Singleton realisieren.
    2. Dann eine Klasse zur Userauthentifizierung schreiben.
    3. Dieser Authentifizierungsklasse dann eine Singleton-Instanz deiner Datenbankklasse beim Erstellen übergeben.
    4. Auf das Erstellen von klassenlosen Funktionen eher verzichten.
    5. Auf den globalen Zugriff auf Klasseneigenschaften ebenfalls verzichten.

    Auf diese Weise kannst du überall, wo Benutzerauthentifizierung gewünscht ist, einfach die Authentifizierungsklasse instanziieren, ihr das DB-Singleton übergeben (durch die Implementierung als Singleton hast du global nur eine Instanz dieser Klasse und nur eine DB-Verbindung, auch wenn du anderswo schon DB-Zugriff machst), und Benutzernamen und Passwort zur Authentifizierung übergeben.

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
  2. echo $begrüßung;

    Neben dem von Sven schon Vorgeschlagenen:

    Hast du das error_reporting auf E_ALL stehen? E_STRICT brauchst du in deinem Fall auch. Falls das in deiner PHP-Version noch nicht in E_ALL enthalten ist, solltest du es aufnehmen (oder gleich error_reporting mit -1 beglücken).

    include 'config.php'; // in der $table_prefix und die DB_[...] Konstanten sind

    Da hast du deine Klassendeklaration drin stehen. Erzeugst du darin auch ein Objekt $db?

    $db->user  = $table_prefix . 'users';

    Wenn nicht, worauf greifst du nun zu? Eine E_STRICT-Warnung tät aufklären.

    function login_right($username, $pwd) {
          $sql = "SELECT COUNT(*) as Anzahl FROM $db->user
      // Fehlermeldungen:
      // - Undefined variable: db...
      // - Trying to get property of non-object...

    Hier bist du nun in einer Funktion, die ihren eigenen Gültigkeitsbereich von Variablen hat. $db ist bisher in ihr nicht definiert und auch nicht per global bekanntgegeben. An der Stelle kann Svens Hinweis auf das Singleton-Pattern Abhilfe schaffen.

    Außerdem greifst du bisher immer nur auf Objekteigenschaften zu. Wenn du etwas Generelles für alle Anwender der Klasse oder von Objekten dieser Klasse verwenden willst, solltest du dir statische Eigenschaften anschauen, die auch im Unterschied zu den Objekteigenschaften Klasseneigenschaften genannt werden. Das Singleton-Pattern im PHP-Handbuch macht auch Gebrauch von solch einer Klasseneigenschaft.

    Weiterhin sieht mir dein Code sehr nach PHP4 aus. Wenn das der Fall ist und es keine zwingenden Gründe gegen PHP5 gibt, solltst du wechseln, denn PHP4 ist schon seit einiger Zeit abgekündigt und kann der Version 5 in Sachen OOP nicht das Wasser reichen. Alle gegebenen Hinweise lassen sich zwar prinzipiell auch mit PHP4 umsetzen, aber da fehlen dir dann Klasseneigenschaften, die dir das Leben leichter machen. Stattdessen muss man statische Variablen in Methoden verwenden, wenn man das gleiche mit PHP4 nachbilden möchte.

    echo "$verabschiedung $name";

    1. Hast du das error_reporting auf E_ALL stehen? E_STRICT brauchst du in deinem Fall auch.
      Falls das in deiner PHP-Version noch nicht in E_ALL enthalten ist, solltest du es aufnehmen
      (oder gleich error_reporting mit -1 beglücken).

      Ah ja, jetzt kommen auch noch folgende Fehlermeldungen:
       - Creating default object from empty value
       - Non-static method db::open() should not be called statically

      Da hast du deine Klassendeklaration drin stehen. Erzeugst du darin auch ein Objekt $db?

      Nein, in der Config, sind nur die DB_HOST, DB_PASS,... PATH, $table_prefix defniniert

      login_right("Lupus", "mypassword");
      Hier bist du nun in einer Funktion, die ihren eigenen Gültigkeitsbereich von Variablen hat.
      $db ist bisher in ihr nicht definiert und auch nicht per global bekanntgegeben.
      An der Stelle kann Svens Hinweis auf das Singleton-Pattern Abhilfe schaffen.

      Hm, wie und wo kann ich lernen mir Singleton umzugehen, ich habe schon Im Manual nachgelesen,
      aber ich bin noch nicht so vertraut mit OOP und darum ist noch vieles neu und unklar - wie Singleton.

      Weiterhin sieht mir dein Code sehr nach PHP4 aus.

      Ja, bisher habe ich immer mit EasyPHP und der 4er Version von PHP gearbeitet, bin aber seit kurzer zeit auf MAMP umgestiegen, die auch PHP 5 beinhaltet.
      Auch habe ich PHP (und OOP) auf einem etwas älteren - aber sehr guten - online Tutorial gelernt (#php/quakeNet).
      Dort werden leider nur die Basics von OOP erklärt und die neuen Möglichkeiten von PHP5 nicht behandelt.
      Kennt ihr gutes Online Tutorial für OOP und PHP?

      Vielen Dank für die Antworten,
      Lupus

      1. echo $begrüßung;

        Ah ja, jetzt kommen auch noch folgende Fehlermeldungen:

        • Creating default object from empty value

        Wie es bei PHP mit einfachen Variablen üblich ist, werden auch Objekte beim ersten Zugriff erstellt. Da du sie einfach so ansprichst, statt ein Objekt einer bestimmten Klasse mit new zu erzeugen wird eine Standard-Klasse verwendet.

        • Non-static method db::open() should not be called statically

        Seit PHP5 sollten die neuen OOP-Features genutzt werden. PHP4 kannte keinen Unterschied beim Aufruf von Methoden. Jede konnte als statische Klassenmethode und als Objektmethode aufgerufen werden. PHP5 unterscheidet nun auch durch die Syntax beide Fälle.

        Da hast du deine Klassendeklaration drin stehen. Erzeugst du darin auch ein Objekt $db?
        Nein, [...]

        So ein Objekt entsteht ja nicht aus heiteren Himmel. Instantiiere eins oder greife auf die statischen Klassenmethoden und -eigenschaften zu, dann aber mit der richtigen Syntax.

        Hm, wie und wo kann ich lernen mir Singleton umzugehen, ich habe schon Im Manual nachgelesen,
        aber ich bin noch nicht so vertraut mit OOP und darum ist noch vieles neu und unklar - wie Singleton.

        Singleton ist ein Programmiermuster. So ein Pattern ist eine Vorgehensweise für immer wiederkehrende Probleme unabhängig von einer konkreten Implementation. Singleton ist so definiert, dass es genau eine Instanz von einem bestimmten Ding gibt. Eine weitere darf nicht erzeugt werden.

        Eine konkrete Implementation ist oft so realisiert, dass der Konstruktor private ist und nicht direkt aufgerufen werden kann. Eine öffentliche Klassenmethode kennt eine Stelle, an der die eine Instanz liegt, z.B. in einer privaten statischen Eigenschaft. Beim ersten Aufruf dieser Methode liegt dort noch nichts und die Methode erstellt ein Objekt, das sie dort ablegt und auch zurückgibt. Alle weiteren Aufrufe dieser Methode geben dann immer wieder diese eine Instanz raus.

        Der wichtigste Vorteil beim Zugriff über die Klassenmethode ist, dass dies in allen Scopes mit der gleichen Syntax erfolgen kann.

        Kennt ihr gutes Online Tutorial für OOP und PHP?

        Ist mir nicht direkt bekannt. Das PHP-Handbuch beschreibt schon mal alle neuen Features http://de.php.net/manual/en/language.oop5.php. Außerdem habe ich mir irgendwann mal http://www.professionelle-softwareentwicklung-mit-php5.de/ verlinkt, es aber nie gelesen. Ich vertraue jedoch dem Autor.

        Die Syntax zu kennen ist nur ein kleiner Teil. Wichtig ist in meinen Augen auch, sich guten Stil anzueignen und zu schauen, wie andere ihre Probleme lösen. Oftmals gibt es schon Muster, wie man günstig an eine bestimmte Aufgabe herangeht.

        Für MySQL bringt PHP ja schon die mysqli-Extension mit, die auch objektorientiert genutzt werden kann. Allerdings gibt es mit PDO einen datenbankübergreifenden Ansatz, der auch noch teilweise anwenderfreundlicher implementiert ist. Es ist im Prinzip also schon alles da, und "muss" nur noch um das Singleton-Pattern ergänzt werden. Wenn man die Grundlagen der OOP einigermaßen kennt, sind fertige Frameworks ansehenswert. Nicht nur, dass da bereits schon viel Funktionalität vorhanden ist, es lassen sich daraus auch noch jede Menge Lösungswege lernen.

        echo "$verabschiedung $name";

        1. Vielen Dank für deine Antwort, dedlfix.
          Ich habe den Code jetzt folgendermassen erweitert:

            
          /*  
           * functions.php  
           */  
            // in der Klasse vor allen Methoden:  
                public $forum = "";  
                public $user = "";  
            // weiter unten eine neue Methode:  
                function setvars() {  
                    $this->forum = '';  
                    $this->user = '';  
                    return true;  
                }  
            
          /*  
           * includes.php  
           */  
            // include config und functions  
            $db = new db('', 'noquery'); // damit keine Automatische Datenbank Abfrage gesendet wird (Methode db erweitert)  
            $db->setvars(); // Die Variablen forum und user für $db Als leere Menge definieren.  
            $db->forum  = $table_prefix . 'forum';  
            $db->user  = $table_prefix . 'users';  
            // Die Variablen überschreiben,... auch wenn das kein schöner Stil ist.  
            // Aber wenn ich direkt in setvars() $this->user = $table_prefix . 'users';  
            // angebe, kann $table_prefix nicht gefunden werden.  
          
          

          So ist die Fehlermeldung "Creating default object from empty value" weg. Gut.
          Aber in der Funktion login_right() wird $db->user trotzdem nicht erkannt und mit den beiden Fehlermeldungen vom ersten Post ausgegeben.
          Ich bin Ratlos. Was übersehe ich? Ich habe doch jetzt schön brav die beiden Variablen in der Klasse definiert und dann über setvars() Aufgerufen.

          1. echo $begrüßung;

            // in der Klasse vor allen Methoden:
                  public $forum = "";
                  public $user = "";
              // weiter unten eine neue Methode:
                  function setvars() {
                      $this->forum = '';
                      $this->user = '';
                      return true;
                  }

            Die Methode setvars() sieht mir in der Form recht überflüssig aus. Die beiden Eigenschaften hast du ja schon mit einem Leerstring Default-Wert versehen. Müssten sie mit einem komplexeren Mechanismus initialisiert werden, bietet sich dafür der Konstruktor an. Nur wenn sie zwischendurch immer mal wieder geleert werden müssen, bietet sich dafür diese Methode an. Im return true sehe ich auch keinen gesteigerten Nutzen.

            $db = new db('', 'noquery'); // damit keine Automatische Datenbank Abfrage gesendet wird (Methode db erweitert)

            Hiermit erzeugst du ein Objekt, das in der Variable $db abgelegt ist. Für Objekte gelten die gleichen Zugriffbeschränkungen wie für alle anderen Variablentypen auch.

            $db->setvars(); // Die Variablen forum und user für $db Als leere Menge definieren.
              $db->forum  = $table_prefix . 'forum';
              $db->user  = $table_prefix . 'users';
              // Die Variablen überschreiben,... auch wenn das kein schöner Stil ist.
              // Aber wenn ich direkt in setvars() $this->user = $table_prefix . 'users';
              // angebe, kann $table_prefix nicht gefunden werden.

            Ah, das ist der Sinn. Natürlich wird $table_prefix in der Methode nicht gefunden, weil Methoden/Funktionen ja ihren eigenen Gültigkeitsbereich haben. Definiere table_prefix als Konstante, wenn darin ein konstanter Wert abgelegt werden soll. Konstanten sind superglobal und überall erreichbar. Es ist jedoch für diesen Anwendungsfall wohl besser, wenn du der Methode setvars() die beiden Werte als Parameter mitgibst. Dann hat sie wenigstens wieder einen Sinn.

            Aber in der Funktion login_right() wird $db->user trotzdem nicht erkannt und mit den beiden Fehlermeldungen vom ersten Post ausgegeben.
            Ich bin Ratlos. Was übersehe ich?

            Immer noch den Geltungsbereich von Variablen. Mit der gängigen Implementierung des Singleton-Musters bekommst du auch das in den Griff.

            $db = db::getdb();  // das ist die Singleton-Methode
              $db->...  // Zugriff auf Eigenschaften und Methoden

            Da PHP bei Objekten immer Referenzen übergibt, erhältst du (bei richtiger Implementation) mit dem Aufruf dieser Klassenmethode immer die Referenz auf die einzig existierende Instanz der Klasse. wann immer du in einem neuen Geltungsbereich diese Instanz benötigst, holst du dir ihre Referenz mit dem Aufruf der Singleton-Methode und legst sie in einer lokalen Variable ab.

            Oder brauchst du mehrere dieser db-Objekte? Dann gibt es dafür das Factory-Pattern. Eine "Fabrik" ist für die Instantiierung von Objekten zuständig. Man übergibt ihr beim Auftrag, ein Objekt zu erstellen, die für die Erstellung nötigen Parameter und erhält ein passendes Objekt zurück. Die Bestellannahme und Auslieferung übernimmt wie beim Singleton-Pattern eine statische Klassenmethode. Die ist in jedem Geltungsbereich aufrufbar.

            echo "$verabschiedung $name";