Pit: Geltungsbereich Variable in Funktion

Hallo,

ich möchte gerne den Verbindungsaufbau zur DB (pdo) über meine config-datei erledigen, weil die ohnehin bei jedem Script aufgerufen wird.

Aufbau:


$user="root";
$passwd="";
$server="localhost";
$name="table";

try {
   $mydb = new PDO("mysql:host=".$server.";dbname=".$name.";charset=utf8;port=3306",$user,$passwd);
   $mydb->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch (PDOException $e) {
    echo 'Verbindung fehlgeschlagen: ' . $e->getMessage();
}

Leider ergibt das in jeder Funktion aber einen Fehler, weil dort die Verbindung nicht bekannt ist. Wenn ich innerhalb der Funktion die config wieder include, dann läufts.

Fehler: Fatal error: Call to a member function query() on a non-object in...

Kann man das erneute includen in jede Funktion irgendwie umgehen?

Pit

  1. Hallo

    ich möchte gerne den Verbindungsaufbau zur DB (pdo) über meine config-datei erledigen, weil die ohnehin bei jedem Script aufgerufen wird.

    Aufbau:

    try {
       $mydb = new PDO("mysql:host=".$server.";dbname=".$name.";charset=utf8;port=3306",$user,$passwd);
       $mydb->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
    } catch (PDOException $e) {
        echo 'Verbindung fehlgeschlagen: ' . $e->getMessage();
    }
    

    Leider ergibt das in jeder Funktion aber einen Fehler, weil dort die Verbindung nicht bekannt ist. Wenn ich innerhalb der Funktion die config wieder include, dann läufts.

    Fehler: Fatal error: Call to a member function query() on a non-object in...

    Kann man das erneute includen in jede Funktion irgendwie umgehen?

    Übergib das Verbindungsobjekt $mydb als Parameter in die Funktionen.

    Tschö, Auge

    --
    Eine Kerze stand [auf dem Abort] bereit, und der Almanach des vergangenen Jahres hing an einer Schnur. Die Herausgeber kannten ihre Leser und druckten den Almanach auf weiches, dünnes Papier.
    Kleine freie Männer von Terry Pratchett
    1. Hi Auge,

      Kann man das erneute includen in jede Funktion irgendwie umgehen?

      Übergib das Verbindungsobjekt $mydb als Parameter in die Funktionen.

      Ersteinmal danke für Deine Hilfe.

      Leider bringt mir das nicht ganz so viel, weil es für mich noch mehr Arbeit bedeuten würde.

      Hintergrund: Ich bin dabei, meine scripte von mysql_ nach PDO umzuschreiben. Bei Deiner Lösung müßte ich nun auch noch alle Funktionsaufrufe berücksichtigen.

      Kann ich das umgehen? Ggf. durch Anhang des Parameters mit einem Defaultwert?

      Oder soll kann man (und sollte man überhaupt??) das Objekt auf global setzen?

      Pit

    2. Hallo,

      Übergib das Verbindungsobjekt $mydb als Parameter in die Funktionen

      Kann man machen, ist aber extrem lästig.

      Man kann für solche zentralen Singletons durchaus auch global verwenden.

      Es gibt nach meiner Meinung 4 Ansätze für SQL in Programmen:

      • Scattered SQL (der klassische "ich mach mal eben" Ansatz) - hier kommt man ohne eine globale Variable für die SQL-Connection kaum aus.
      • SQL Zugriffsfunktionen: Alle SQL Zugriffe sind in Funktionen gekapselt, und die liegen in einem Include. Damit hat man eine bessere Chance, gleiche Queries zu erkennen und wiederzuverwenden. In diesen Funktionen wird das DB-Zugriffsobjekt als global angegeben.
      • SQL-Access Klasse(n): Die Zugriffsfunktionen sind als Methoden in einer oder mehreren Klassen angeordnet. Die Objekte dieser Klassen erzeugt man nicht selbst, sondern über eine Factory. Die Factory kennt das DB-Objekt und steckt es in die Access-Klassen hinein. Also auch was globales, aber nicht einfach als Variable, sondern als ein Service. In deiner Config-Datei kannst Du das DB-Objekt erzeugen und in die Factory hineinstecken. Oder die Factory erzeugt das DB-Objekt beim ersten SQL Zugriff im Hintergrund.
      • Ein ORM Framework. Aber das schmeißt Dir alles über den Haufen.

      Rolf

      --
      sumpsi - posui - clusi
      1. Hallo

        Übergib das Verbindungsobjekt $mydb als Parameter in die Funktionen

        Kann man machen, ist aber extrem lästig.

        Man kann für solche zentralen Singletons auch global verwenden.

        Kann man natürlich auch machen. Das habe ich aber bewusst unterschlagen, weil die Nennung bei mir eine gewisse Enge in den Schuhspitzen erzeugt. Ich mag's halt nicht.

        <buzzwordstream>
        Oder man lernt Begriffe wie Inversion-of-Control und Dependency Injection, und injiziert sich das DB-Zugriffsobjekt in die Arbeitsklassen. Ist aber nicht so ganz aus dem Handgelenk erledigt...
        </buzzwordstream>

        Oder so, aber dazu kann ich nichts beitragen.

        Tschö, Auge

        --
        Eine Kerze stand [auf dem Abort] bereit, und der Almanach des vergangenen Jahres hing an einer Schnur. Die Herausgeber kannten ihre Leser und druckten den Almanach auf weiches, dünnes Papier.
        Kleine freie Männer von Terry Pratchett
  2. Tach!

    Kann man das erneute includen in jede Funktion irgendwie umgehen?

    Um auf globale Variablen in Funktionen zuzugreifen, muss man diese dort entweder mit dem Schlüsselwort global bekanntgeben, oder man greift über das $GLOBALS-Array zu, das ist superglobal und von überall ansprechbar.

    Schön ist diese Vorgehensweise aber nicht. Man muss vor dem Funktionsaufruf wissen, was man erst zu initialisieren hat, damit die Funktion arbeiten kann. Besser ist es, man fordert die Abhängigkeiten über Parameter ein, damit man das nicht vergessen kann. Man nennt das Dependency Injection. Allerdings ist das dann auch Arbeit, die Abhängigkeiten herumzureichen. Und so hat man Dependency-Injection-Frameworks entwickelt, die einem die Arbeit größtenteils abnehmen. Lohnt sich aber erst ab großen Projekten, wenn es nicht bereits im sowieso schon verwendeten Framework enthalten ist.

    dedlfix.

    1. Hello,

      Um auf globale Variablen in Funktionen zuzugreifen, muss man diese dort entweder mit dem Schlüsselwort global bekanntgeben, oder man greift über das $GLOBALS-Array zu, das ist superglobal und von überall ansprechbar.

      Oder man baut sich ein Singleton, was ja dank Static-Variablen auch in der prozeduralen Programmierung von PHP geht. Das hat den Vorteil, dass sie Bestand hat, wenn sie einmal gesetzt wurde.

      Ist später die Variable === NULL, existiert keine DB-Connection.

      Liebe Grüße
      Tom S.

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
  3. Moin Pit,

    was passiert denn mit folgender kleiner Änderung?

    $user="root";
    $passwd="";
    $server="localhost";
    $name="table";
    $mydb = NULL;    // ← beachte
    
    try {
       $mydb = new PDO("mysql:host=".$server.";dbname=".$name.";charset=utf8;port=3306",$user,$passwd);
       $mydb->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
    } catch (PDOException $e) {
        echo 'Verbindung fehlgeschlagen: ' . $e->getMessage();
    }
    

    … und dann in deiner Funktion

    function something(/* possible parameters */) {
        global $mydb;
    }
    

    Viele Grüße
    Robert

  4. Lieber Pit,

    die Lösung mit global hat man Dir ja schon angeboten. Aber dazu wurde auch gesagt, dass das nicht optimal ist. Wie aber kann man die Idee mit dem globalen Zugriff auf eine Variable besser lösen?

    Wenn man sein Programm als großes Objekt definiert, dann kann man in diesem Objekt sogenannte Eigenschaften definieren. Innerhalb des Objekts sind diese Eigenschaften wie globale Variablen verfügbar.

    Vergleiche:

    $myVar = 3;
    
    function say () {
        global $myVar;
        echo $myVar;
    }
    
    class MyProgram {
    
        private $myVar = 3;
    
        public  function say () {
            echo $this->myVar;
        }
    }
    
    $myProgram = new MyProgram;
    $myProgram->say();
    

    Klar, für dieses klitzekleine Beispiel ist der Schreibaufwand mit einer globalen Variable viel geringer. Aber je größer ein Projekt wird, desto besser gelingt es den Überblick zu behalten, wenn es in objektorientierter Weise modularisiert wird.

    Jetzt möchtest Du eine PDO-Instanz nutzen. Da hast Du schon ein Objekt. Warum packst Du es nicht als eine Eigenschaft in Dein zum Objekt umgewandeltes Programm? Anstatt $myVar könntest Du $this->db verwenden - oder so.

    Liebe Grüße,

    Felix Riesterer.

    1. ... übrigens:

      Dieses Problem habe ich nur bei der PDO-Lösung gehabt.

      Bei der ursprünglichen mysql_ - Lösung ist die Verbindung "bekannt".

      Wie kommt das eigentlich?

      Pit

      1. Tach!

        Bei der ursprünglichen mysql_ - Lösung ist die Verbindung "bekannt".

        Wie kommt das eigentlich?

        Weil man das so programmiert hat. Wenn keine Verbindungskennung den Funktionen übergeben wurde, hat PHP eine der bereits geöffneten verwendet.

        dedlfix.

        1. Weil man das so programmiert hat. Wenn keine Verbindungskennung den Funktionen übergeben wurde, hat PHP eine der bereits geöffneten verwendet.

          Danke dedlfix,

          hat das prozedurale mysqli das auch übernommen?

          Pit

          1. Tach!

            hat das prozedurale mysqli das auch übernommen?

            Nein, deswegen musst du es ja zu jedem Funktionsaufruf angeben. Während es bei mysql ein optionaler Parameter war, der an letzter Stelle stand, ist es bei mysqli der erste und ein Pflichtparameter.

            dedlfix.