Ja. Der Punkt ist, dass du bei Includes keinen abgeschotteten Gültigkeitsbereich für Variablennamen hast.
Sagen wir, du möchtest die dritte Potenz (x³) der ersten 10 positiven Ganzzahlen (1-10) ermitteln und ausgeben.
Dein Hauptprogramm sähe wahrscheinlich etwa so aus:
PHP:
<?php
for ($i = 1; $i <= 10; $i++) {
$base = $i;
$exp = 3;
include 'inc.exp.php';
echo $result . '<br>';
}
Das Script in inc.exp.php erwartet, dass die Werte für Basis und Exponent in den Variablen $base und $exp vorliegen. Es füllt dann $result mit dem entsprechenden Ergebnis.
Der Code in inc.exp.php:
PHP:
<?php
$result = $base;
for ($i = 0; $i < $exp; $i++) {
$result *= $base;
}
if ($exp === 0) {
$result = 1;
}
Das ergibt beim Ausführen allerdings eine Endlosschleife.
PHP führt im Prinzip diesen Code aus:
PHP:
<?php
for ($i = 1; $i <= 10; $i++) {
$base = $i;
$exp = 3;
$result = $base;
for ($i = 0; $i < $exp; $i++) {
$result *= $base;
}
if ($exp === 0) {
$result = 1;
}
echo $result . '<br>';
}
In der inneren for-Schleife wird $i immer wieder auf den Wert 3 gesetzt (der Wert von $exp). Und wenn $i den Wert 3 hat, bricht die äußere Schleife logischerweise nicht ab.
Wie lösen wir das Problem?
Wir nennen $i in der inkludierten Datei einfach $j. Könnten wir machen. Dann müssten wir allerdings aufpassen, das Include nie in eine Schleife zu schreiben, die mit $j zählt. Das mag in diesem simplen Fall eine Möglichkeit sein, aber je komplexer die Anwendung wird, desto mehr Variablennamen verbauen wir uns auf diese Weise.
Wir speichern bei Aufruf der Include-Datei alle gesetzten Variablen auf einem Stack (eine Art Zwischenspeicher) und „restaurieren“ sie am Ende der Include-Datei wieder. Das liest sich etwas kompliziert und diese Lösung ist auch nicht ganz einfach. Im folgenden Beispiel sind die drei Variablen $__STACK, $__IN und $__OUT interne, reservierte Variablen, die nur zum Datenaustausch zwischen Includes und dem eigentlichen Programm genutzt werden sollen.
inc.exp.php:
PHP:
<?php
unset($__OUT); # Verhindert, dass $__OUT bei extract() über-
# schrieben wird
$__STACK[] = get_defined_vars(); # Sämtliche definierten Variablen auf Stack
# schieben
$base = $__IN[0]; # 1. Übergabeparameter ist Basis
$exp = $__IN[1]; # 2. Übergabeparameter ist Exponent
$result = $base; # Dieser Teil wie gehabt. Die hier eingesetzten
# lokalen Variablen werden beim Aufruf von
for ($i = 0; $i < $exp; $i++) { # extract() wieder mit denjenigen Werten
$result *= $base; # überschrieben, die sie vor der Ausführung
} # des Include-Codes hatten
if ($exp === 0) {
$result = 1;
}
$__OUT[0] = $result; # Ergebnis der Berechnung in Variable $__OUT
# speichern
extract(array_pop($__STACK)); # Alten Zustand aller Variablenwerte von vor
# dem Ausführen des Includes wiederherstellen
Und der Aufruf:
PHP:
<?php
// Reservierte Variablen
$__STACK = array();
$__IN = array();
$__OUT = array();
for ($i = 1; $i <= 10; $i++) {
$base = $i;
$exp = 3;
$__IN[0] = $base; # Beim Einbinden des Includes werden die Übergabewer-
$__IN[1] = $exp; # te in der Variablen $__IN gespeichert
include 'inc.exp.php';
$result = $__OUT[0]; # Die Rückgabe liegt äquivalent dazu in $__OUT vor
echo $result . '<br>';
}
Auf diese Weise sind Includes schachtelbar. Hier ein Include, das zu einer Zahl eine andere Zahl addiert:
PHP:
<?php
unset($__OUT); # Verhindert, dass $__OUT bei extract() über-
# schrieben wird
$__STACK[] = get_defined_vars(); # Sämtliche definierten Variablen auf Stack
# schieben
$n = $__IN[0]; # 1. Übergabeparameter ist Ausgangszahl
$k = $__IN[1]; # 2. Übergabeparameter ist zu addierende Zahl
$result = $n;
for ($i = 0; $i < $k; $i++) {
$result++;
}
$__OUT[0] = $result; # Ergebnis der Berechnung in Variable $__OUT
# speichern
extract(array_pop($__STACK)); # Alten Zustand aller Variablenwerte von vor
# dem Ausführen des Includes wiederherstellen
Dieses Include in inc.exp.php integriert:
PHP:
<?php
unset($__OUT); # Verhindert, dass $__OUT bei extract() über-
# schrieben wird
$__STACK[] = get_defined_vars(); # Sämtliche definierten Variablen auf Stack
# schieben
$base = $__IN[0]; # 1. Übergabeparameter ist Basis
$exp = $__IN[1]; # 2. Übergabeparameter ist Exponent
$result = $base; # Dieser Teil wie gehabt. Die hier eingesetzten
# lokalen Variablen werden beim Aufruf von
for ($i = 0; $i < $exp; $i++) { # extract() wieder mit denjenigen Werten
$result *= $base; # überschrieben, die sie vor der Ausführung
} # des Include-Codes hatten
if ($exp === 0) {
$result = 1;
}
$__IN[0] = $result;
$__IN[1] = 10;
include 'inc.addk.php';
$result = $__OUT[0];
$__OUT[0] = $result; # Ergebnis der Berechnung in Variable $__OUT
# speichern
extract(array_pop($__STACK)); # Alten Zustand aller Variablenwerte von vor
# dem Ausführen des Includes wiederherstellen
Die Pointe dabei ist natürlich, dass PHP mit der Funktionssyntax genau dieses Zwischenspeichern bereits belegter Variablen und die Zuweisung der Werte für $__IN und $__OUT automatisch übernimmt.
PHP:
<?php
function addk($n, $k)
{
$result = $n;
for ($i = 0; $i < $k; $i++) {
$result++;
}
return $result;
}
function exponent($base, $exp)
{
$result = $base;
for ($i = 0; $i < $exp; $i++) {
$result *= $base;
}
if ($exp === 0) {
$result = 1;
}
$result = addk($result, 10);
return $result;
}
for ($i = 1; $i <= 10; $i++) {
$result = exponent($i, 3);
echo $result . '<br>';
}
Intern passiert dabei grob das, was ich mit $__STACK, $__IN und $__OUT ziemlich vereinfacht umrissen habe.
Glücklicherweise hatten die Entwickler des PHP-Interpreters (wie die Entwickler fast jeder anderen Programmiersprache) Mitleid und haben dieses Verwalten der Variablen über spezielle Syntaxkonstrukte (function) abstrahiert.
-
Unterprogramm