PHP Variablen übergabe in welcher Form sinnvoll?
bearbeitet von 1unitedpower> 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](https://forum.selfhtml.org/self/2016/feb/15/php-variablen-uebergabe-in-welcher-form-sinnvoll/1661034#m1661034) 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:
~~~php
// 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.
~~~php
// 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.
~~~php
// 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()`{: .language-php} 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()`{: .language-php} 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:
~~~php
// 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();
~~~
> Soll etwa jedes Objekt die Konfigurationsdatei lesen und was machst Du bei einem, sagen wir mal schlankem, funktionalen 100-Zeiler? Den mit Objekten auf 300 Zeilen aufblasen?
Dependency-Injection und Inversion-of-Control für in der Regel sogar zu schlankerem Code.