Suche…


Was ist Typ Jonglieren?

PHP ist eine locker getippte Sprache. Das bedeutet, dass Operanden in einem Ausdruck standardmäßig nicht vom selben (oder kompatiblen) Typ sein müssen. Sie können beispielsweise eine Zahl an eine Zeichenkette anhängen und erwarten, dass sie funktioniert.

var_dump ("This is example number " . 1);

Die Ausgabe wird sein:

Zeichenfolge (24) "Dies ist Beispielnummer 1"

PHP erreicht dies durch das automatische Konvertieren inkompatibler Variablentypen in Typen, die die Ausführung der angeforderten Operation ermöglichen. Im obigen Fall wird das Ganzzahlliteral 1 in eine Zeichenfolge umgewandelt, sodass es mit dem vorhergehenden Zeichenfolgenliteral verkettet werden kann. Dies wird als Typ Jonglieren bezeichnet. Dies ist eine sehr leistungsfähige Funktion von PHP, aber es ist auch eine Funktion, die Sie zu einer Menge Haare ziehen kann, wenn Sie sich dessen nicht bewusst sind, und sogar zu Sicherheitsproblemen führen.

Folgendes berücksichtigen:

if (1 == $variable) {
    // do something
}

Die Absicht scheint zu sein, dass der Programmierer prüft, ob eine Variable den Wert 1 hat. Was passiert jedoch, wenn $ variable stattdessen "anderthalb" hat? Die Antwort könnte Sie überraschen.

$variable = "1 and a half";
var_dump (1 == $variable);

Das Ergebnis ist:

bool (wahr)

Warum ist das passiert? Dies liegt daran, dass PHP erkannt hat, dass die Zeichenfolge "anderthalb" keine ganze Zahl ist. Sie muss jedoch sein, um sie mit der ganzen Zahl 1 zu vergleichen ganze Zahl. Dies geschieht, indem alle Zeichen am Anfang der Zeichenfolge, die in Ganzzahl umgewandelt werden können, übernommen werden. Es stoppt, sobald es auf ein Zeichen trifft, das nicht als Zahl behandelt werden kann. Daher wird "anderthalb" in die Ganzzahl 1 umgewandelt.

Zugegeben, dies ist ein sehr erfundenes Beispiel, aber es dient dazu, das Problem zu veranschaulichen. In den nächsten Beispielen werden einige Fälle behandelt, in denen ich auf Fehler gestoßen bin, die durch das Typjonglieren in der realen Software verursacht wurden.

Lesen aus einer Datei

Beim Lesen aus einer Datei möchten wir wissen können, wann wir das Ende dieser Datei erreicht haben. Wenn fgets() wissen, dass fgets() am Ende der Datei false zurückgibt, verwenden wir dies möglicherweise als Bedingung für eine Schleife. Wenn die aus dem letzten Lesevorgang zurückgegebenen Daten jedoch als boolesches false ausgewertet werden, kann dies dazu führen, dass unsere Dateilese-Schleife vorzeitig beendet wird.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

Wenn die gelesene Datei eine leere Zeile enthält, wird die while Schleife an diesem Punkt beendet, da die leere Zeichenfolge als boolean false ausgewertet wird.

Stattdessen können wir explizit nach dem booleschen Wert false suchen, indem Sie strikte Gleichheitsoperatoren verwenden :

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

Beachten Sie, dass dies ein erfundenes Beispiel ist. Im wirklichen Leben würden wir die folgende Schleife verwenden:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

Oder ersetzen Sie das Ganze durch:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

Überraschungen wechseln

Switch-Anweisungen verwenden einen nicht strengen Vergleich, um Übereinstimmungen zu ermitteln. Dies kann zu bösen Überraschungen führen . Betrachten Sie zum Beispiel die folgende Anweisung:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

Dies ist eine sehr einfache Anweisung und funktioniert wie erwartet, wenn $name eine Zeichenfolge ist. Andernfalls können Probleme auftreten. Wenn zum Beispiel $name eine ganze Zahl von 0 , tritt während des Vergleichs ein Typ-Jonglieren auf. Es ist jedoch der Literalwert in der case-Anweisung, der jongliert wird, nicht die Bedingung in der switch-Anweisung. Die Zeichenfolge "input 1" wird in eine Ganzzahl 0 konvertiert, die mit dem Eingangswert von Ganzzahl 0 übereinstimmt. Wenn Sie einen Wert von Integer 0 angeben, wird der erste Fall immer ausgeführt.

Es gibt einige Lösungen für dieses Problem:

Explizites Casting

Der Wert kann typisiert in einen String vor dem Vergleich:

switch ((string)$name) {
...
}

Oder eine Funktion, von der bekannt ist, dass sie einen String zurückgibt, kann auch verwendet werden:

switch (strval($name)) {
...
}

Beide Methoden stellen sicher, dass der Wert denselben Typ hat wie der Wert in den case Anweisungen.

vermeiden switch

Die Verwendung einer if Anweisung gibt uns die Kontrolle darüber, wie der Vergleich durchgeführt wird, und ermöglicht die Verwendung strenger Vergleichsoperatoren :

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

Striktes Tippen

Seit PHP 7.0 können einige der schädlichen Auswirkungen des Typjonglierens durch strikte Typisierung gemindert werden. Durch das Einschließen dieser declare als erste Zeile der Datei erzwingt PHP die Deklaration von Parametertypen und die Deklaration von Rückgabetypen durch TypeError einer TypeError Ausnahme.

declare(strict_types=1);

Beispielsweise wird dieser Code bei Verwendung von Parametertypdefinitionen beim Ausführen eine abfangbare Ausnahme vom Typ TypeError :

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

Dieser Code verwendet ebenfalls eine Rückgabetypdeklaration. Es wird auch eine Ausnahme ausgelöst, wenn versucht wird, etwas anderes als eine Ganzzahl zurückzugeben:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow