Globale Variablen

PHP war ursprünglich eine rein prozedurale Programmiersprache, zu der objektorientierte Sprachfeatures nachträglich hinzugefügt wurden. Im Rückblick kann man sagen, dass es eine ganze Weile gedauert hat, bis man mit PHP "richtig" objektorientiert programmieren konnte. Das ist der Grund dafür, dass weit verbreitete PHP-Software wie beispielsweise Drupal oder Wordpress auf den ersten Blick objektorientiert aussehen, hinter den Kulissen aber immer noch sehr stark in ihrer prozeduralen Vergangenheit verwurzelt sind.

Die Objektorientierung in PHP ist anderen Programmiersprachen mindestens ebenbürtig.

Dabei sollten wir zuallererst auf die Verwendung von globalen Variablen verzichten. Schauen wir uns dazu ein Beispiel an:

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($a): void
{
    var_dump($a);
}
globalAndLocalVariable.php

Wie viele Variablen $a gibt es hier? Richtig, es sind zwei. Einmal gibt es oben die globale Variable $a und innerhalb der Funktion gibt es den Parameter und damit eine lokale Variable, die ebenfalls $a heißt. Man nennt das übrigens Verschattung.

Dieses Programm gibt zweimal test aus, weil die beiden Variablen $a - gewissermaßen zufällig - den gleichen Wert haben:

globalAndLocalVariable.php:9:
string(4) "test"
globalAndLocalVariable.php:5:
string(4) "test"
Ausgabe von globalAndLocalVariable.php
globalAndLocalVariable.php ausführen

Wir könnten das zweite $a auch umbenennen, beispielsweise in $b:

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($b): void
{
    var_dump($b);
}
globalAndRenamedLocalVariable.php

Das funktioniert - wenig überraschend - genauso:

globalAndLocalVariable.php:9:
string(4) "test"
globalAndLocalVariable.php:5:
string(4) "test"
Ausgabe von globalAndLocalVariable.php
globalAndLocalVariable.php ausführen

Um einen Punkt zu machen, bleiben wir aber bei der ursprünglichen Doppelung der Namen, also zweimal $a. Eine lokale Variable können wir jederzeit ändern, sie "gehört" uns ja exklusiv:

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($a): void
{
    $a .= '!';

    var_dump($a);
}
globalAndModifiedLocalVariable.php

Wenn wir den Wert von $a innerhalb der Funktion ändern, wirkt sich das nicht auf die globale Variable $a aus:

globalAndModifiedLocalVariable.php:11:
string(5) "test!"
globalAndModifiedLocalVariable.php:5:
string(4) "test"
Ausgabe von globalAndModifiedLocalVariable.php
globalAndModifiedLocalVariable.php ausführen

Wir sehen zuerst test!, weil der Funktionsaufruf some_function() vor dem globalen var_dump() ausgeführt wird. Jetzt machen wir mal so richtig Unsinn und sagen PHP, dass wir innerhalb der Funktion some_function() auf die globale Variable $a zugreifen möchten:

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($a): void
{
    global $a;

    var_dump($a);
}
globalVariable.php

Das sollten wir so natürlich nicht machen, weil es stiftet Verwirrung. Dennoch bleibt das Ergebnis erst einmal unverändert, weil sowohl das globale als auch das lokale $a ohnehin den gleichen Wert haben:

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($a): void
{
    global $a;

    var_dump($a);
}
globalVariable.php

Aber was passiert, wenn wir nun innerhalb der Funktion $a ändern?

<?php declare(strict_types=1);

$a = 'test';
some_function($a);
var_dump($a);

function some_function($a): void
{
    global $a;

    $a .= '!';

    var_dump($a);
}
modifiedGlobalVariable.php

Schauen wir mal:

modifiedGlobalVariable.php:13:
string(5) "test!"
modifiedGlobalVariable.php:5:
string(5) "test!"
Ausgabe von modifiedGlobalVariable.php
modifiedGlobalVariable.php ausführen

Autsch. Jetzt rächt es sich bitter, dass wir in der Funktion mit der globalen anstelle einer lokalen Variable arbeiten, denn die scheinbar lokale Änderung hat nun globale Auswirkungen. Wenn wir sowas in einer hinreichend komplexen "echten" Anwendung machen, dann gute Nacht. Man spricht hier übrigens von einem Seiteneffekt, was eine falsche deutsche Übersetzung des englischen Begriffs side effect ist, also eigentlich "Nebenwirkung" im Sinne von unerwünschte Nebenwirkung wie bei einem Medikament bedeutet.

Unsere Funktion hat uns gerade den globalen Systemzustand kaputt gemacht.

Warum wir global vermeiden

Genau das ist das Problem mit globalen Variablen. In einem kleinen Beispielprogramm sieht es vielleicht nicht wirklich schlimm aus, wenn man globale Variablen verwendet. In einer echten Anwendung, gerade wenn mehrere verschiedene Entwickler daran arbeiten, ist die Verwendung von globale Variablen ganz schnell nicht mehr beherrschbar. Das liegt vor allem daran, dass jeder von überall und jederzeit nicht nur lesend, sondern sogar schreibend auf alle globalen Variablen zugreifen kann.

Bevor Du eine globale Variable verwendest, kommst Du zu Fuß nach Wolfratshausen und fragst mich um Erlaubnis.

Die prozedurale Programmierung in PHP hat keine wirklich sinnvollen Antworten auf die Frage, wie man globale Variablen vermeiden kann. Es gibt neben dem Array keine wirklich gute Möglichkeit, Datenstrukturen zu definieren, wenn man stdClass verwendet, ist das auch nicht besser. Wir würden also immer längere Parameterlisten bekommen, wenn wir jeder Funktion immer alle Daten zur Verfügung stellen würden, die sie benötigt. Und gerade die Rückgabewerte sind ein Problem, wenn wir nicht entweder Arrays zurückgeben oder mit Referenzen arbeiten wollen.

Das ist doch ein guter Grund dafür, objektorientiert zu programmieren, denn dann können wir Objekte sowohl als Parameter als auch als Rückgabewerte verwenden.

Noch mehr Globalität

Das Problem von globalen Variablen und damit globalem Systemzustand wird übrigens noch schlimmer, wenn wir diesen dauerhaft speichern, weil dann haben wir persistenten globalen Systemzustand.

Neben den globalen Variablen kennt PHP übrigens auch noch die sogenannten superglobalen Variablen, die auch ohne explizite global-Deklaration in jedem Gültigkeitsbereich automatisch verfügbar sind.

Fragen zur Lernkontrolle

  1. Welche Vorteile hat die explizite Deklaration von globalen Variablen innerhalb von Funktionen und Methoden?
  2. Ein Kollege schlägt vor, Funktionen ohne Parameter zu schreiben und stattdessen globale Variablen zur Übergabe sowie Rückgabe zu verwenden. Welche Nachteile hat das?
  3. Ein Grundproblem von globalen Variablen ist deren Veränderbarkeit. Diskutiere, ob man daher stattdessen globalen Konstanten verwenden sollte.