1unitedpower: PHP Variablen übergabe in welcher Form sinnvoll?

Beitrag lesen

Moin!

Beide Varianten sind gleich schlecht im Hinblick auf die Tatsache, dass globale Variablen

Die Syntax mit global habe ich angeblich bereits vergessen, da ich diese niemals benutze.

Globale Konstanten sollte man trotzdem eher vermeiden.

Nana. Also $GLOBAL['config']['sonstwas'] nicht. Konstanten nicht ... wie will denn der Herr eine Konfiguration an seine Funktionen übergeben?

Dann hättest du den zweiten Fall vorliegen, den Sven eben beschrieben hat. Du hättest ein Problem oder zumindest eine temporale Abhängigkeit geschaffen: Der Erfolg der Funktion ist davon abhängig, dass die Konfiguration vorher geladen wurde. Ich konstruiere mal ein kleines Beispiel: Es gibt drei Dateien: foo.php, case1.php, case2.php. foo.php soll eine wichtige Funktion in unserer Anwendung enthalten, die auf Datenbankzugriff angewiesen ist. case1.php und case2.php rufen diese Funktion jeweils auf, ein Mal auf gutmütige Weise und ein Mal absichtlich falsch:

// foo.php
function logToDatabase () {
  global $config;
  $db = $condig->db;
  // mach etwas mit sinnvolles mit der Datenbank
}

// case1.php
require('foo.php');
$config = loadConfiguration();
logToDatabase(); // Alles gut

// case2.php
require('foo.php');
logToDatabase(); // Übler Anwendungscrash
$config = loadConfiguration(); 

Die Tükke hierbei ist, dass du zum Zeitpunkt der Definition der Funktion nicht vorausahnen kannst, dass das Laden der Konfiguration und der der Aufruf der Funktion in der richtigen Reihenfolge geschieht. Damit hägt der Erfolgsfall der Funktion implizit von der aurfrufenden Stelle ab. Du könntest in der Funktion nun natürlich abfangen, ob die Konfiguration schon geladen ist und dem Entwickler an dieser Stelle einen hilfreichen Hinweis hinterlassen.

// foo.php
function logToDatabase () {
  global $config;
  if (! $config->db instanceof PDO) {
    throw new Exception("Keine aktive Datenbankverbindung! Globale Konfiugration schon geladen?");
  } else {
    // mach etwas mit sinnvolles mit der Datenbank
  }
}

// case1.php
require('foo.php');
$config = loadConfiguration();
logToDatabase(); // Alles Top

// case2.php
require('foo.php');
logToDatabase(); // Mööp! Fehler
$config = loadConfiguration();

Damit vereitelst du auch, dass das Programm in ungelenkte Bahnen gerät. Man sieht aber auch, dass dieser Ansatz die ursprüngliche Funktion deutlich aufbläht, und wenn das nun in jeder Funktion gemacht werden muss, die Datenbankzugriff oder eine andere globale Einstellung benötigt, dann führt das zu stark fragmentiertem Kontrollfluss innerhalb der Anwendung. Eine heute weit verbreitere Lösung ist es daher, die Kontrolle über die Einhaltung der Reihenfolge an die aufrufende Stelle abzutreten. Bei Funktionen kann das auf sehr einfache Art geschehen, indem man ihnen einen zusätzlichen Parameter gibt. Die Abhängigkeit, die vorher implizit war, ist nun explizit.

// foo.php
function logToDatabase (PDO $db) {
  // mach etwas mit sinnvolles mit der Datenbank
}

// case1.php
require('foo.php');
$config = loadConfiguration();
logToDatabase($config->db);     // Alles Top

// case2.php
require('foo.php');
logToDatabase();                // Mööp! Fehler schon vor der Laufzeit
$config = loadConfiguration();

Diese Variante ist noch weniger fehleranfällig als die vorherige: In den ersten beiden Fällen kann der Fehler erst zur Laufzeit entdeckt werden, wenn die Funktion logToDatabase() tatsächlich aufgerufen wird. In diesem letzten Beispiel kann der Fehler schon vor der eigentlichen Laufzeit durch statische Programmanalyse entdeckt werden, die heutige Buildtools und IDEs standardmäßig durchführen.

Eine positive Nebenwirkung ist, dass die Funktion logToDatabase() generischer ist als zuvor und nun auch mit anderen Datenbanken aufgerufen werden kann. Man profitiert also zusätzlich von erhöhter Wiederverwendbarkeit. Auf der anderen Seite kann diese erhöhte Flexibilität natürlich zum Nervtöter und erneuter Fehlerquelle werden, wenn sie unerwünscht ist und jedesmal wieder mit dem selben zusätzlichen Parameter aufgerufen werden muss. Dagegen könnte man an geeigneter Stelle eine neue Funktion erzeugen, die intern die ursprüngliche Funktion mit entsprechenden Vorbelegungen aufruft. Das könnte dann etwa so aussehen:

// foo.php
function logToDatabase (PDO $db) {
  // mach etwas mit sinnvolles mit der Datenbank
}

// case1.php
require('foo.php');
$config = loadConfiguration();
$logToProduction = function () use ($config) {
  logToDatabase($config->db);
}
$logToProduction();