Geben Sie Casting-Variablen in PHP ein. Was ist der praktische Grund dafür?

45

PHP hat, wie die meisten von uns wissen, eine schwache Typisierung . Für diejenigen, die dies nicht tun, sagt PHP.net:

PHP benötigt (oder unterstützt) keine explizite Typdefinition in der Variablendeklaration. Der Typ einer Variablen wird durch den Kontext bestimmt, in dem die Variable verwendet wird.

Lieben Sie es oder hassen Sie es, PHP wandelt Variablen on-the-fly um. Der folgende Code ist also gültig:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

Mit PHP können Sie auch explizit eine Variable wie folgt umwandeln:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

Das ist alles cool ... aber für mein Leben kann ich mir keinen praktischen Grund dafür vorstellen.

Ich habe kein Problem mit starken Eingaben in Sprachen wie Java, die dies unterstützen. Das ist in Ordnung und ich verstehe es vollkommen. Außerdem bin ich mir der Nützlichkeit von Typhinweisen in Funktionsparametern bewusst und verstehe sie vollständig .

Das Problem, das ich beim Typgießen habe, wird durch das obige Zitat erklärt. Wenn PHP Typen nach Belieben austauschen kann , kann dies auch nach dem Erzwingen des Castens eines Typs geschehen. Dies ist im Handumdrehen möglich, wenn Sie für eine Operation einen bestimmten Typ benötigen. Das macht Folgendes gültig:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

Also, was ist der Punkt?


Nehmen Sie dieses theoretische Beispiel einer Welt, in der benutzerdefiniertes Typ-Casting in PHP Sinn macht :

  1. Sie erzwingen die Umwandlung einer Variablen $fooals int(int)$foo.
  2. Sie versuchen, einen Zeichenfolgenwert in der Variablen zu speichern $foo.
  3. PHP wirft eine Ausnahme !! ← Das wäre sinnvoll. Plötzlich besteht der Grund für benutzerdefiniertes Typ-Casting!

Die Tatsache, dass PHP die Dinge nach Bedarf umdreht, macht den Punkt der benutzerdefinierten Typumwandlung vage. Die folgenden zwei Codebeispiele sind beispielsweise äquivalent:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

Erraten Sie ein Jahr, nachdem Sie diese Frage ursprünglich gestellt hatten, wer Typografie in einer praktischen Umgebung verwendet hat? Mit freundlichen Grüßen.

Die Anforderung bestand darin, Geldwerte auf einer Website für ein Restaurantmenü anzuzeigen. Das Design der Site erforderte, dass nachfolgende Nullen abgeschnitten wurden, sodass die Anzeige etwa wie folgt aussah:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

Der beste Weg, diese Verschwendung zu tun, um die Variable als Float umzuwandeln:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP schneidet die nachgestellten Nullen des Floats ab und setzt den Float dann als String zur Verkettung um.

Stephen
quelle
Dies -> $ value = $ value. 'TaDa!'; Würde $ value vor der Zuweisung zum endgültigen Wert von $ value auf string zurücksetzen. Es ist keine große Überraschung, dass Sie einen Typ-Cast erhalten, wenn Sie einen Typ-Cast erzwingen. Sie sind sich nicht sicher, worum es geht, wenn Sie fragen, worum es geht?
Chris
"# 3. PHP löst eine Ausnahme aus !! <--- Das wäre sinnvoll." Eigentlich würde das überhaupt keinen Sinn ergeben. Das ist nicht einmal ein Problem in Java, JavaScript oder einer anderen mir bekannten C-Syntax-Sprache. Wer in seinem Verstand würde das als wünschenswertes Verhalten sehen? Möchtest du überall(string) Abgüsse haben ?
Nicole
@Renesis: Du verstehst mich falsch. Ich meinte damit, dass eine Ausnahme nur dann ausgelöst wird, wenn ein Benutzer eine Variable typisiert hat. Das normale Verhalten (bei dem PHP das Casting für Sie durchführt) würde natürlich keine Ausnahme auslösen. Ich versuche zu sagen, dass das benutzerdefinierte Typ-Casting umstritten ist , aber wenn eine Ausnahme geworfen würde, wäre es plötzlich sinnvoll.
Stephen
Wenn Sie sagen, $intval.'bar'wirft eine Ausnahme, ich bin immer noch nicht einverstanden. Das macht in keiner Sprache eine Ausnahme. (Alle mir bekannten Sprachen führen entweder eine automatische Besetzung oder eine aus .toString()). Wenn Sie sagen, $intval = $stringvalwirft eine Ausnahme, dann sprechen Sie über eine stark typisierte Sprache. Ich wollte nicht unhöflich klingen, also tut mir leid, wenn ich es tat. Ich denke nur, es widerspricht dem, was jeder Entwickler gewohnt ist und ist viel, viel weniger bequem.
Nicole
@Stephen - Ich habe nach einigen Untersuchungen eine Antwort gepostet. Wirklich interessante Ergebnisse - ich dachte, dass 2 der Fälle sicherlich einen Zweck für das Casting zeigen würden, aber PHP ist noch seltsamer als ich dachte.
Nicole

Antworten:

32

In einer schwach typisierten Sprache gibt es eine Typumwandlung, um Mehrdeutigkeiten in typisierten Operationen zu beseitigen, wenn der Compiler / Interpreter andernfalls die Reihenfolge oder andere Regeln verwenden würde, um eine Annahme darüber zu treffen, welche Operation verwendet werden soll.

Normalerweise würde ich sagen, dass PHP diesem Muster folgt, aber von den Fällen, die ich überprüft habe, hat sich PHP in jedem gegenintuitiv verhalten.

In diesen Fällen wird JavaScript als Vergleichssprache verwendet.

String Concatentation

Offensichtlich ist dies in PHP kein Problem, da es separate Operatoren für Zeichenfolgenverkettung ( .) und Addition ( +) gibt.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

String-Vergleich

In Computersystemen ist "5" häufig größer als "10", da es nicht als Zahl interpretiert wird. Nicht so in PHP, das, auch wenn beide Zeichenfolgen sind, erkennt, dass es sich um Zahlen handelt, und die Notwendigkeit einer Besetzung beseitigt.

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

Funktionsunterschrift eingeben

PHP implementiert eine reine Typprüfung für Funktionssignaturen, die jedoch leider so fehlerhaft ist, dass sie wahrscheinlich nur selten verwendet werden kann.

Ich dachte, ich könnte etwas falsch machen, aber ein Kommentar in den Dokumenten bestätigt, dass andere eingebaute Typen als Array nicht in PHP-Funktionssignaturen verwendet werden können - obwohl die Fehlermeldung irreführend ist.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

Und im Gegensatz zu jeder anderen Sprache, die ich kenne, kann null nicht mehr an dieses Argument übergeben werden, selbst wenn Sie einen Typ verwenden, den es versteht ( must be an instance of array, null given). Wie blöd.

Boolesche Interpretation

[ Bearbeiten ]: Dieser ist neu. Ich dachte an einen anderen Fall und wieder ist die Logik von JavaScript umgekehrt.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

Abschließend ist der einzige nützliche Fall, an den ich denken kann, ... (Trommelwirbel)

Geben Sie Kürzung ein

Mit anderen Worten, wenn Sie einen Wert eines Typs (beispielsweise eine Zeichenfolge) haben und ihn als einen anderen Typ (int) interpretieren möchten und erzwingen möchten, dass er zu einem der gültigen Werte dieses Typs wird:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

Ich kann nicht sehen, was für eine Aufwertung (zu einem Typ, der mehr Werte umfasst) Sie jemals wirklich gewinnen würde.

So , während ich mit Ihnen vorgeschlagenen Verwendung des Schreibens nicht einverstanden ist (Sie im Wesentlichen vorschlagen , statische Typisierung , aber mit der Mehrdeutigkeit , die nur wenn es Kraft Guss in eine Art würde es einen Fehler aus - was zu Verwirrung führen würde), ich denke , es ist ein gutes ist Frage, weil anscheinend Casting in PHP sehr wenig Sinn hat.

Nicole
quelle
Okay, wie wäre es E_NOTICEdann? :)
Stephen
@Stephen E_NOTICEmag in Ordnung sein, aber für mich ist der zweideutige Zustand von Bedeutung - woher wissen Sie, wenn Sie sich ein Bit Code ansehen , wenn sich die Variable in diesem Zustand befindet (woanders gegossen)? Außerdem fand ich eine andere Bedingung und fügte sie meiner Antwort hinzu.
Nicole
1
Bei der booleschen Auswertung wird in PHP-Dokumenten eindeutig angegeben, was bei der Auswertung zu booleschen Werten als falsch angesehen wird, und sowohl leere Zeichenfolgen als auch eine Zeichenfolge "0" werden als falsch angesehen. Selbst wenn sich das seltsam anfühlt, ist es ein normales und erwartetes Verhalten.
Jacek Prucia
um ein bisschen Verwirrung zu echo "010" == 010echo "0x10" == 0x10
stiften
1
Beachten Sie, dass ab PHP 7 die Anmerkungen zu skalaren Typhinweisen in dieser Antwort ungenau sind.
John V.
15

Sie mischen die Konzepte von schwachen / starken und dynamischen / statischen Typen.

PHP ist schwach und dynamisch, aber Ihr Problem ist das Konzept des dynamischen Typs. Das heißt, Variablen haben keinen Typ, Werte schon.

Ein Typ-Casting ist ein Ausdruck, der einen neuen Wert eines anderen Typs des Originals erzeugt. es macht nichts mit der Variablen (wenn eine beteiligt ist).

Die einzige Situation, in der ich regelmäßig Cast-Werte eingebe, sind numerische SQL-Parameter. Sie sollten jeden Eingabewert, den Sie in SQL-Anweisungen einfügen, bereinigen / entziehen oder (viel besser) parametrisierte Abfragen verwenden. Wenn Sie jedoch einen Wert möchten, der eine Ganzzahl sein MUSS, ist es viel einfacher, ihn einfach zu konvertieren.

Erwägen:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

Wenn ich die erste Zeile weglasse, $idwäre dies ein einfacher Vektor für die SQL-Injektion. Die Besetzung stellt sicher, dass es sich um eine harmlose Ganzzahl handelt. Jeder Versuch, SQL einzufügen, würde einfach zu einer Abfrage nach führenid=0

Javier
quelle
Ich werde das akzeptieren. Nun, was den Nutzen von Type Casting angeht?
Stephen
Es ist lustig, dass Sie die SQL-Injection aufrufen. Ich habe mit jemandem über SO gestritten, der diese Technik verwendet, um Benutzereingaben zu bereinigen. Doch welches Problem löst diese Methode, das es mysql_real_escape_string($id);noch nicht gibt?
Stephen
es ist kürzer :-) natürlich verwende ich für strings parametrisierte abfragen oder (wenn ich die alte mysql-erweiterung verwende) entkomme ich sie.
Javier
2
mysql_real_escape_string()hat eine Schwachstelle, die nichts mit Strings wie '0x01ABCDEF' zu tun hat (dh hexadezimale Darstellung einer Ganzzahl). In einigen Multibyte-Codierungen (nicht zum Glück Unicode) kann eine solche Zeichenfolge verwendet werden, um die Abfrage zu unterbrechen (da sie von MySQL zu etwas ausgewertet wird, das ein Zitat enthält). Deshalb ist weder mysql_real_escape_string()noch is_int()die beste Wahl für den Umgang mit ganzzahligen Werten. Typecasting ist.
Mchl
Ein Link mit weiteren Details: ilia.ws/archives/…
Mchl
4

Eine Verwendung für Typumwandlungen in PHP, die ich gefunden habe:

Ich entwickle eine Android-App, die HTTP-Anforderungen an PHP-Skripte auf einem Server sendet, um Daten aus einer Datenbank abzurufen. Das Skript speichert Daten in Form eines PHP-Objekts (oder eines assoziativen Arrays) und wird als JSON-Objekt an die App zurückgegeben. Ohne Typ Casting würde ich so etwas erhalten:

{ "user" : { "id" : "1", "name" : "Bob" } }

Bei Verwendung von PHP-Typumwandlung (int)für die Benutzer-ID beim Speichern des PHP-Objekts wird dies stattdessen an die App zurückgegeben:

{ "user" : { "id" : 1, "name" : "Bob" } }

Wenn das JSON-Objekt dann in der App analysiert wird, erspart es mir, die ID in eine Ganzzahl zu analysieren!

Sehr nützlich.

Ryan
quelle
Ich hatte nicht daran gedacht, Daten für externe, stark typisierte Systeme zu formatieren. +1
Stephen
Dies gilt insbesondere für JSON-Gespräche mit externen Systemen wie Elasticsearch. Ein json_encode () - ed-Wert "5" ergibt ganz andere Ergebnisse als der Wert 5.
Johan Fredrik Varen
3

Ein Beispiel dafür sind Objekte mit einer __toString Methode: $str = $obj->__toString();vs $str = (string) $obj;. In der Sekunde wird viel weniger getippt, und das Besondere ist die Zeichensetzung, deren Eingabe länger dauert. Ich denke auch, dass es besser lesbar ist, obwohl andere vielleicht anderer Meinung sind.

Ein anderer macht ein Einzelelement - Array: array($item);vs (array) $item;. Dadurch wird ein beliebiger skalarer Typ (Ganzzahl, Ressource usw.) in ein Array eingefügt.
Wenn $itemes sich bei einem Objekt um ein Objekt handelt, werden seine Eigenschaften zu Schlüsseln für seine Werte. Ich denke jedoch, dass die Konvertierung von Objekt-> Array etwas seltsam ist: Private und geschützte Eigenschaften sind Teil des Arrays und werden umbenannt. So zitieren Sie die PHP-Dokumentation : Private Variablen haben den Klassennamen vor dem Variablennamen. Geschützte Variablen haben ein '*' vor dem Variablennamen.

Eine andere Verwendung ist die Konvertierung von GET / POST-Daten in geeignete Datenbanktypen. MySQL kann dies selbst handhaben, aber ich denke, dass die ANSI-kompatibleren Server die Daten möglicherweise ablehnen. Der Grund, warum ich nur Datenbanken erwähne, ist, dass in den meisten anderen Fällen die Daten irgendwann einer Operation unterzogen werden, die ihrem Typ entspricht (dh mit int / floats werden normalerweise Berechnungen durchgeführt usw.).

Alan Pearce
quelle
Dies sind großartige Beispiele für die Funktionsweise von Typguss. Ich bin jedoch nicht davon überzeugt, dass sie ein Bedürfnis erfüllen . Ja, Sie können ein Objekt in ein Array konvertieren, aber warum? Ich denke, weil Sie dann die unzähligen PHP-Array-Funktionen auf dem neuen Array verwenden könnten, aber ich kann nicht ergründen, wie das nützlich wäre. Außerdem erstellt PHP normalerweise Zeichenfolgenabfragen, die an eine MySQL-Datenbank gesendet werden sollen, sodass der Variablentyp irrelevant ist (automatische Zeichenfolgenkonvertierung von intoder floaterfolgt beim Erstellen der Abfrage). (array) $itemist ordentlich , aber nützlich?
Stephen
Ich stimme tatsächlich zu. Als ich sie abtippte, dachte ich, dass ich an einige Verwendungszwecke denken würde, aber ich tat es nicht. Wenn die Parameter für das Datenbankmaterial Teil der Abfragezeichenfolge sind, haben Sie Recht, Casting hat keinen Zweck. Wenn Sie jedoch parametrisierte Abfragen verwenden (was immer eine gute Idee ist), können Sie die Parametertypen angeben.
Alan Pearce
Aha! Möglicherweise haben Sie mit "Parametrisierte Abfragen" einen gültigen Grund angegeben.
Stephen
0

Dieses Skript:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

läuft einwandfrei für script.php?tags[]=one, schlägt aber fehl script.php?tags=one, da _GET['tags']im ersten Fall ein Array zurückgegeben wird, im zweiten Fall jedoch nicht. Da das Skript so geschrieben ist, dass es ein Array erwartet (und Sie weniger Kontrolle über die an das Skript gesendete Abfragezeichenfolge haben), kann das Problem gelöst werden, indem das Ergebnis aus den folgenden Quellen entsprechend umgewandelt wird _GET:

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}
beldaz
quelle
0

Es kann auch als schnelle und unsaubere Methode verwendet werden, um sicherzustellen, dass nicht vertrauenswürdige Daten nicht beschädigt werden, z.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Offensichtlich ist dies fehlerhaft und wird auch implizit vom Vergleichsoperator in der if-Anweisung behandelt. Dies ist jedoch hilfreich, um sicherzustellen, dass Sie genau wissen, was Ihr Code tut.

Gruffputs
quelle
1
Nur dass es nicht kaputt geht. Selbst wenn $_POST['amount']ein Garbage String enthalten wäre, würde PHP auswerten, dass er nicht größer als Null ist. Wenn es eine Zeichenfolge enthielte, die eine positive Zahl darstellte, würde es wahr ausgewertet.
Stephen
1
Nicht ganz richtig. Angenommen, der Betrag in Höhe von $ wird an einen Drittanbieter innerhalb der Bedingung weitergeleitet, der eine Nummer erhalten muss. Wenn jemand $ _POST ['amount'] = "100 bobbins" übergeben würde, würde das Entfernen (float) weiterhin zulassen, dass die Bedingung übergeben wird, aber $ amount wäre keine Zahl.
Gruffputs
-2

Eine "Verwendung" von PHP-Re-Casting-Variablen im laufenden Betrieb, die ich häufig sehe, ist das Abrufen von Daten aus externen Quellen (Benutzereingaben oder Datenbanken). Damit können Programmierer (ich habe nicht gesagt, dass Entwickler ) die verschiedenen Datentypen, die aus verschiedenen Quellen verfügbar sind, ignorieren (oder gar nicht lernen).

Ein Codierer (beachten Sie, dass ich nicht Entwickler gesagt habe), dessen Code ich geerbt habe und der immer noch verwaltet, scheint nicht zu wissen, dass ein Unterschied zwischen der Zeichenfolge "20", die in der $_GETSupervariablen zurückgegeben wird, und der Ganzzahloperation besteht,20 + 20 wenn sie hinzugefügt wird der Wert in der Datenbank. Sie hat nur das Glück, dass PHP .für die Verkettung von Zeichenfolgen verwendet und nicht +wie jede andere Sprache, weil ich gesehen habe, dass ihr Code zwei Zeichenfolgen (eine varcahrvon MySQL und einen Wert von $_GET) "hinzufügt" und eine Ganzzahl erhält.

Ist das ein praktisches Beispiel? Nur in dem Sinne, dass Programmierer nicht wissen, mit welchen Datentypen sie arbeiten. Ich persönlich hasse es.

dotancohen
quelle
2
Ich verstehe nicht, wie diese Antwort der Diskussion einen Mehrwert verleiht. Die Tatsache, dass PHP es einem Ingenieur (oder Programmierer oder Programmierer, was Sie haben) ermöglicht, mathematische Operationen an Strings durchzuführen, ist in dieser Frage bereits sehr klar.
Stephen
Vielen Dank, Stephen. Ich habe vielleicht zu viele Wörter verwendet, um zu sagen: "Mit PHP können Leute, die nicht wissen, was ein Datentyp ist, Anwendungen erstellen, die unter idealen Bedingungen das tun, was sie erwarten."
Dotancohen