Karl: Globale Variablen

Hallo,

ich bin noch am Anfang mit der Programmierung deshalb habt Nachsicht mit mir ;-)

Wenn ich in PHP globale Variablen definiere, die ich z.B. in all meinen Scripten verwenden will, mache ich das zur Zeit so:

1. Einbinden meiner z.B. Konfigurationsdatei:

  
require_once("include/config.php");  

Darin sind dann z.B. meine ganzen globalen Variablen gespeichert:

  
$CONFIG_SERVER = "localhost";  

2. Ausgabe der Variable z.B. in irgendeiner Funktion

  
function machwas(){  
 	if ($GLOBALS["CONFIG_SERVER"] == "localhost") {  
		$result = "lokal";  
	} else {  
		 $result = "extern";  
	}  
	return $result;  
}  

Jetzt meine Frage: In verschiedenen Beispielen habe ich gesehen das manche Entwickler die Globalen Variablen in der Konfigurationsdatei so definieren:

  
$GLOBALS["CONFIG_SERVER"] = "localhost";  

und manchmal so:

  
$CONFIG_SERVER = "localhost";  

Bei der zweiten Version werden dann die Variablen außerhalb von Funktionen sogar manchmal ohne $GLOBALS[], genutzt.
Beides Funktioniert, nur was ist richtig bzw. der bessere Stil?

Danke
Karl

  1. Hallo,

    Beides Funktioniert, nur was ist richtig bzw. der bessere Stil?

    Sich wegzubewegen von globalen Variablen ;)

    Deine config.php kann auch ein array zurückgeben, schön kompakt und an beliebiger Stelle abrufbar, ebenda, wo es grad gebraucht wird ($cfg = require 'config.php';).

    MfG

    1. Aloha ;)

      Deine config.php kann auch ein array zurückgeben, schön kompakt und an beliebiger Stelle abrufbar, ebenda, wo es grad gebraucht wird ($cfg = require 'config.php';).

      Schon - aber in wiefern soll das besser sein, das Array immer wieder per require einzubinden, als die Werte einmal ins GLOBALS-Array zu werfen und dann zu verwenden? Das ist doch eigentlich - wie heißt es so schön im Schwäbischen - g'hupft wie g'sprunga.

      Ich stimme dir zu, dass der Einsatz globaler Variablen im Einzelfall gut abgewägt werden sollte, es gibt aber nunmal Werte, die sinnvollerweise in GLOBALS gespeichert werden können. Es sollte nur nicht überhand nehmen.

      Verstanden hätte ich es eher, wie delfix es imho schonmal getan hat, zu raten, auf GLOBALS innerhalb von Funktionen zugunsten von Parametern zu verzichten (und auch das ist imho nicht immer die effektivste, wenn auch vieleicht sauberste Lösung).

      Grüße,

      RIDER

      --
      Camping_RIDER a.k.a. Riders Flame a.k.a. Janosch Zoller
      ch:? rl:| br:> n4:? ie:% mo:| va:) js:) de:> zu:) fl:( ss:| ls:[
      1. Hakuna matata!

        Deine config.php kann auch ein array zurückgeben, schön kompakt und an beliebiger Stelle abrufbar, ebenda, wo es grad gebraucht wird ($cfg = require 'config.php';).

        Schon - aber in wiefern soll das besser sein, das Array immer wieder per require einzubinden, als die Werte einmal ins GLOBALS-Array zu werfen und dann zu verwenden? Das ist doch eigentlich - wie heißt es so schön im Schwäbischen - g'hupft wie g'sprunga.

        Der globale Gültigkeitsbreich wird nicht zugemüllt und das macht Kollisionen mit anderen Code-Fragmenten unwahrscheinlich. Insbesondere mit so generischen Variablennamen wie $cfg oder $config sind Namenskollisionen vorprogrammiert.

        In PHP würde ich dem Problem aber eher mit Namespaces begegnen, als mit der Lösung, die hotti hier unterbreitet. Aber beides dürfte besser sein, als das Vogelstrauß-Prinzip.

        Globale Variablen liefern zudem immer nur eine sehr kurzsichtige Lösung, auf das Problem, dass es zu bewältigen gilt. Ich habe immer wieder die Erfahrung gemacht, dass eine globale Variable, die bei ihrer Einführung irgendwann mal zweckmäßig erschien, in späteren Entwicklungsphasen zu einem Problem geworden ist. Als klassisches Beispiel: Man sieht häufig, dass der gesamte Datenbank-Verkehr eines PHP-Programms über eine globale Variable oder ein Singelton-Objekt abgewickelt wird. Später steht man vor der Aufgabe einen Datenabgleich mit einer zweiten Datenbank zu machen, dann steht man plötzlich vor dem Problem, wie man mit den zwei Datenbank-Verbindungen umgehen kann, ohne das ganze Programm umzustricken.

        Vorausschauende Software-Entwicklung ist ein schwieriges Thema und man kann sich schnell darin verlieren. Aber im Falle von globalen Variablen gibt es viele verhätlnismäßig einfache Lösungen, die sich mit hoher Wahscheinlichkeit bezahlt machen werden.

        Und ich bin noch nie auf ein Problem gestoßen, dass sich ohne globale Variablen nicht genauso simpel lösen ließ.

        --
        “All right, then, I'll go to hell.” – Huck Finn
        1. jetzt bin ich zwar um ein paar Erkenntnise reicher aber scheinbar gibts da keine Wahrheit;?
          Trotzdem Danke!

          1. Aloha ;)

            jetzt bin ich zwar um ein paar Erkenntnise reicher aber scheinbar gibts da keine Wahrheit;?
            Trotzdem Danke!

            Tja, das ist eben die Sache. Die absolute Wahrheit wirst du nicht finden. Du findest maximal gute Gründe für diese oder jene Art, Dinge zu regeln. Eine Abwägung, was zum eigenen Stil und der Aufgabe passt, muss letztlich jeder selbst vornehmen. Wie in jedem Handwerk.

            Grüße,

            RIDER

            --
            Camping_RIDER a.k.a. Riders Flame a.k.a. Janosch Zoller
            ch:? rl:| br:> n4:? ie:% mo:| va:) js:) de:> zu:) fl:( ss:| ls:[
            1. Hakuna matata!

              Tja, das ist eben die Sache. Die absolute Wahrheit wirst du nicht finden. Du findest maximal gute Gründe für diese oder jene Art, Dinge zu regeln. Eine Abwägung, was zum eigenen Stil und der Aufgabe passt, muss letztlich jeder selbst vornehmen. Wie in jedem Handwerk.

              Und wenn man dann einmal eine Entscheidung gefällt hat, dann sollte man innerhalb des Projekts auch Konsistenz zeigen und nicht ständig zwischen den Möglichkeiten wechseln. Solche Konventionen können sehr hilfreich sein, denn dann müssen die Vor- und Nachteile nicht jedesmal aufs Neue abgewogen werden und es gibt keine Überraschungen beim Lesen des Queltextes.

              --
              “All right, then, I'll go to hell.” – Huck Finn
          2. jetzt bin ich zwar um ein paar Erkenntnise reicher aber scheinbar gibts da keine Wahrheit;?
            Trotzdem Danke!

            Es gibt Wahrheiten:

            1. Vermeide globale Variablen unbedingt.
            2. Konfigurationswerte müssen selten allen Komponenten komplett bekannt sein. Eine Datenbankkomponente braucht beispielsweise keine Infos für das Senden von Mails.
            3. Konfigurationswerte sollten nicht vom Code änderbar sein.
            4. Dependency Injection!

            Aus diesen und weiteren Erkenntnissen folgt, dass die Konfiguration nicht ganz so simpel ist, aber letztendlich doch in erkennbare Phasen zerfällt.

            1. Irgendwo sind die Werte persistiert. Das können Dinge wie eine INI-Datei, ein PHP-Code mit einem Array oder sowas sein.

            2. Es gibt einen Mechanismus, an diese gesammelten Konfigurationswerte so heranzukommen, dass sie von Code genutzt werden können. Beispielsweise mit parse_ini_file(), require() oder etwas komplexerem (weil die Daten komplexer gespeichert sind).

            3. Auf der anderen Seite gibt es ganz individuelle Stellen im Code, die genau einen (vielleicht abzählbar mehr als einen, aber definitiv niemals "alle") Werte aus der Konfiguration benötigen. Dieser Wert sollte dorthin von außen übergebbar sein, ohne dass man außen wissen muss, was intern damit passiert, und ohne dass man innen wegen irgendeiner globalen Variablenbezeichnung Angst haben muss, durch Veränderung etwas kaputt zu machen. Typische Umsetzungen wären die Übergabe als Parameter im Funktionsaufruf (ja, bei jedem Aufruf erneut), durch Aufrufen einer Konfigurationsfunktion für eine Gruppe von Funktionen (idealerweise ist das eine Klasse, die kann Werte intern versteckt ablegen), oder über den Konstruktor (für Konfigurationswerte, die dauerhaft gelten, ist das die einzig korrekte Möglichkeit).

            4. Wie kommt jetzt der Konfigurationswert aus Punkt 2 an die Stelle, wo er gebraucht wird in Punkt 3: Die ideale Lösung ist hier noch nicht gefunden, es hängt davon ab, was man bereits einsetzt oder einzusetzen gewillt ist. Die klassische Lehre von Dependency Injection würde die Konfigurationswerte einfach als ebenfalls zu injectendes Element betrachten, d.h. beim Initialisieren des DI-Konstrukts würden alle Konfigurationswerte ebenfalls initialisiert, und durch den außerdem definierten Objektbaum verwendet. Man könnte die Konfiguration aber auch als Konstanten definieren und dort ansprechen, wo man die Objekte baut - nicht IN den Objekten (ein fester Konstantenname ist genauso eine globale Abhängigkeit, wie eine globale Variable), sondern in den Fabriken. In jedem Fall ist dieser Bereich nach meinem Gefühl noch etwas fummelig.

            1. Hi,

              du hast ein fachlich hilfreich von mir bereits erhalten. Ich will aber den Hauptpunkt nochmals herausgreifen und damit betonen.

              1. Dependency Injection!
              2. Wie kommt jetzt der Konfigurationswert aus Punkt 2 an die Stelle, wo er gebraucht wird in Punkt 3: Die ideale Lösung ist hier noch nicht gefunden, es hängt davon ab, was man bereits einsetzt oder einzusetzen gewillt ist. Die klassische Lehre von Dependency Injection würde die Konfigurationswerte einfach als ebenfalls zu injectendes Element betrachten, d.h. beim Initialisieren des DI-Konstrukts würden alle Konfigurationswerte ebenfalls initialisiert, und durch den außerdem definierten Objektbaum verwendet.

              Um DI zu erklären verweise ich im Kontext PHP mal auf PHP: The right way. Gibt sicherlich bessere Ressourcen, um DI zu erklären, aber PHP: The Right Way sollte man IMHO als PHP-Entwickler sowieso mal gesehen haben.

              Weiterhin musst du die Konfigurationswerte nicht beim Initialisieren des DI-containers mit initialisieren. Du kannst auch ein proxy-Objekt mit Lazy-Loading darüber aufbauen. Alternativ injectest du eine Konfig-Factory, die lazy die eigentliche Konfiguration lädt, wenn sie das erste mal benutzt wird.

              Wichtig ist im Falle Proxy-Objekt: die Konfiguration kann kein primitiver Wert sein, sondern ist ein Objekt. Sonst funktioniert das Proxy-Pattern nicht.

              Viele Grüße,
              Matti

      2. Aloha ;)

        Deine config.php kann auch ein array zurückgeben, schön kompakt und an beliebiger Stelle abrufbar, ebenda, wo es grad gebraucht wird ($cfg = require 'config.php';).

        Schon - aber in wiefern soll das besser sein, das Array immer wieder per require einzubinden, als die Werte einmal ins GLOBALS-Array zu werfen und dann zu verwenden? Das ist doch eigentlich - wie heißt es so schön im Schwäbischen - g'hupft wie g'sprunga.

        Kommt drauf an, wo Du reinspringst, wenn Deine Wahl auf ein Haifischbecken fällt, ist es egal, ob Du beim Springen eine gute Figur abgegeben hast, gefressen wirst Du so oder so ;)

        Ich stimme dir zu, dass der Einsatz globaler Variablen im Einzelfall gut abgewägt werden sollte, es gibt aber nunmal Werte, die sinnvollerweise in GLOBALS gespeichert werden können. Es sollte nur nicht überhand nehmen.

        Es kommt auf den Umgang mit globalen Variablen an, ein schönes Beispiel ist $@ (globale Perl Variable) darin steht der Grund für eine Exception, die möglicherweise in einer bestimmten Methode fallen kann, also:

        Anstatt $@ zu befragen, ob eine Exception gefallen ist, wird unmittelbar beim Funktionsaufruf anhand des return geprüft, ob die Funktion durchgelaufen ist, erst danach wird $@ herangezogen zur Begründung.

        Globale Variablen sind keine Schande. Es ist nur immer gut zu wissen, dass sie eben global veränderbar sind und dass da auch mal was anderes als erwartet drinstehen kann.

          
        $addr =~ /name: (\w+)/;  
        $name = $1; # Vorsicht!!!!  
        
        

        Besser:

          
        $name = do{  
           $addr =~ /name: (\w+)/ or die "No match";  
           $1;  
        } || print "Die Adressangabe gibt den Namen nicht her, Begründung: $@" ;  
        
        

        Verstanden hätte ich es eher, wie delfix es imho schonmal getan hat, zu raten, auf GLOBALS innerhalb von Funktionen zugunsten von Parametern zu verzichten (und auch das ist imho nicht immer die effektivste, wenn auch vieleicht sauberste Lösung).

        Full ack.

  2. Aloha ;)

    Bei der zweiten Version werden dann die Variablen außerhalb von Funktionen sogar manchmal ohne $GLOBALS[], genutzt.
    Beides Funktioniert, nur was ist richtig bzw. der bessere Stil?

    Beide Varianten sind gleichwertig. Imho war es so, dass es in Urzeiten so war, dass es das superglobale GLOBALS-Array nicht gab, da konnte innerhalb von Funktionen nur per global-Schlüsselwort auf globale Variablen zugegriffen werden.

    Beide Schreibweisen sind für mich daher auch von der Richtigkeit her gleichwertig.

    Ich empfehle aus einem einzigen Grund die Schreibweise $GLOBALS['var'] gegenüber $var: Das erstere zeigt weniger Fehlerpotential, da man keine andere Schreibweise innerhalb von Funktionen benötigt (bzw. das potenziell problematische global-Keyword vermeiden kann).

    Grüße,

    RIDER

    --
    Camping_RIDER a.k.a. Riders Flame a.k.a. Janosch Zoller
    ch:? rl:| br:> n4:? ie:% mo:| va:) js:) de:> zu:) fl:( ss:| ls:[