2010-09-12 15 views
12

Nach etwas Arbeit in C und Java war ich mehr und mehr genervt von den Wild-West-Gesetzen in PHP. Was ich wirklich finde, dass PHP fehlt, sind strikte Datentypen. Die Tatsache, dass string ('0') == (int) 0 == (boolean) false ist, ist ein Beispiel.PHP Typecasting - Gut oder schlecht?

Sie können sich nicht darauf verlassen, welchen Datentyp eine Funktion zurückgibt. Sie können Argumente einer Funktion nicht auf einen bestimmten Typ zwingen, was zu einem nicht strikten Vergleich führen kann, der zu etwas Unerwartetem führt. Alles kann erledigt werden, aber es öffnet sich immer noch für unerwartete Fehler.

Ist es gut oder schlecht, typisierte Argumente für eine Methode einzugeben? Und ist es gut, die Rückkehr zu tippen?

IE

public function doo($foo, $bar) { 
    $foo = (int)$foo; 
    $bar = (float)$bar; 
    $result = $bar + $foo; 
    return (array)$result; 
} 

Das Beispiel ist ziemlich dumm und ich habe es nicht getestet, aber ich denke, jeder auf die Idee kommt. Gibt es irgendeinen Grund dafür, dass der PHP-Gott den Dateityp so umsetzt, wie er will, und Leute, die keine Datentypen kennen, PHP benutzen?

+1

Sie können denken, dass schwache Typisierung (wie in PHP, Javascript, Smalltalk, C, C++, etc.) ein Fehler ist, aber es ist Teil des Designs von PHP, und daher unwahrscheinlich, zu ändern. Es ist auch eine der Eigenschaften, die PHP zu einer C-Type-Sprache macht. – GZipp

+2

@GZopp: C und C++ sind keine schwach typisierten Sprachen. –

+0

Sie können tatsächlich verlangen, dass Argumente zu einem bestimmten Typ gehören, aber es funktioniert nur mit Klassen und Arrays, nicht mit anderen integrierten Typen. http://www.php.net/manual/en/functions.arguments.php#98425 – mwhite

Antwort

20

Für besser oder schlechter, loose-Typisierung ist „Die PHP-Way“. Viele der eingebauten und die meisten der Sprachkonstrukte werden auf den Typen funktionieren, die Sie ihnen geben - still (und oft gefährlich), indem sie hinter die Kulissen geworfen werden, damit die Dinge (irgendwie) zusammenpassen.

Da ich selbst von einem Java/C/C++ - Hintergrund herrühre, war das Modell für die lose Eingabe von PHP immer eine Quelle der Frustration für mich. Aber im Laufe der Jahre habe ich festgestellt, dass ich, wenn ich PHP schreiben muss, einen besseren Job machen kann (d. H. Sauberer, sicherer, testbarer Code), indem ich die "Lockerheit" von PHP anerkenne, anstatt sie zu bekämpfen; und ich lande deswegen einen glücklicheren Affen.

Casting ist wirklich grundlegend für meine Technik - und (IMHO) ist es die einzige Möglichkeit, sauberen, lesbaren PHP-Code zu erstellen, der gemischte Argumente in einer gut verständlichen, testbaren, deterministischen Art behandelt.

Der Hauptpunkt (den Sie auch klar verstehen) ist, dass Sie in PHP nicht einfach davon ausgehen können, dass ein Argument der Typ ist, den Sie erwarten. Dies kann schwerwiegende Folgen haben, die Sie wahrscheinlich erst nach dem Produktivstart Ihrer App feststellen.

Um diesen Punkt zu veranschaulichen:

<?php 

function displayRoomCount($numBoys, $numGirls) { 
    // we'll assume both args are int 

    // check boundary conditions 
    if(($numBoys < 0) || ($numGirls < 0)) throw new Exception('argument out of range'); 

    // perform the specified logic 
    $total = $numBoys + $numGirls; 
    print("{$total} people: {$numBoys} boys, and {$numGirls} girls \n"); 
} 

displayRoomCount(0, 0); // (ok) prints: "0 people: 0 boys, and 0 girls" 

displayRoomCount(-10, 20); // (ok) throws an exception 

displayRoomCount("asdf", 10); // (wrong!) prints: "10 people: asdf boys, and 10 girls" 

Ein Ansatz zur Lösung dieses die Typen zu beschränken ist, dass die Funktion übernehmen kann, eine Ausnahme auszulösen, wenn ein ungültiger Typ erkannt wird. Andere haben diesen Ansatz bereits erwähnt. Es passt gut zu meiner Ästhetik von Java/C/C++, und ich habe diesen Ansatz jahrelang in PHP verfolgt. Kurz gesagt, es ist nichts falsch daran, aber es geht gegen "The PHP Way", und nach einer Weile fühlt es sich an, als würde man flussaufwärts schwimmen.

Als Alternative bietet Casting eine einfache und saubere Methode, um sicherzustellen, dass sich die Funktion für alle möglichen Eingaben deterministisch verhält, ohne dass für jeden einzelnen Typ spezifische Logik geschrieben werden muss.

Mit Gießen, unserem Beispiel wird jetzt:

<?php 

function displayRoomCount($numBoys, $numGirls) { 
    // we cast to ensure that we have the types we expect 
    $numBoys = (int)$numBoys; 
    $numGirls = (int)$numGirls; 

    // check boundary conditions 
    if(($numBoys < 0) || ($numGirls < 0)) throw new Exception('argument out of range'); 

    // perform the specified logic 
    $total = $numBoys + $numGirls; 
    print("{$total} people: {$numBoys} boys, and {$numGirls} girls \n"); 
} 

displayRoomCount("asdf", 10); // (ok now!) prints: "10 people: 0 boys, and 10 girls" 

Die Funktion nun wie erwartet verhält. In der Tat ist es einfach zu zeigen, dass das Verhalten der Funktion nun für alle möglichen Eingaben gut definiert ist. Dies liegt daran, dass die Cast-Operation für alle möglichen Eingaben klar definiert ist. Die Casts stellen sicher, dass wir immer mit ganzen Zahlen arbeiten; und der Rest der Funktion ist so geschrieben, dass sie für alle möglichen ganzen Zahlen wohldefiniert ist.

Rules for type-casting in PHP are documented here, (siehe die typspezifischen Links in der Mitte der Seite - zB: "Converting to Integer").

Dieser Ansatz hat den zusätzlichen Vorteil, dass sich die Funktion jetzt in einer Weise verhält, die mit anderen PHP-Einbauten und Sprachkonstrukten übereinstimmt. Zum Beispiel:

// assume $db_row read from a database of some sort 
displayRoomCount($db_row['boys'], $db_row['girls']); 

werden gut funktionieren, trotz der Tatsache, dass $db_row['boys'] und $db_row['girls'] tatsächlich Strings sind die numerischen Werte enthalten. Dies ist konsistent mit der Art, wie der durchschnittliche PHP-Entwickler (der kein C, C++ oder Java kennt) davon ausgehen wird, dass es funktioniert.


Als Rückgabewert zum Gießen: es gibt sehr wenig Punkt dabei, es sei denn, Sie wissen, dass Sie einen potenziell Mischtyp-Variable haben, und Sie wollen immer sicherstellen, dass der Rückgabewert eine bestimmte Art ist. Dies ist häufiger an Zwischenpunkten im Code und nicht an dem Punkt, an dem Sie von einer Funktion zurückkehren.

Ein praktisches Beispiel:

<?php 

function getParam($name, $idx=0) { 
    $name = (string)$name; 
    $idx = (int)$idx; 

    if($name==='') return null; 
    if($idx<0) $idx=0; 

    // $_REQUEST[$name] could be null, or string, or array 
    // this depends on the web request that came in. Our use of 
    // the array cast here, lets us write generic logic to deal with them all 
    // 
    $param = (array)$_REQUEST[$name]; 

    if(count($param) <= $idx) return null; 
    return $param[$idx]; 
} 

// here, the cast is used to ensure that we always get a string 
// even if "fullName" was missing from the request, the cast will convert 
// the returned NULL value into an empty string. 
$full_name = (string)getParam("fullName"); 

Sie erhalten die Idee.


Es gibt ein paar der Gotcha von

  • PHP Casting Mechanismus bewusst zu sein, ist nicht intelligent genug, die "no-op" cast zu optimieren. So Casting immer verursacht eine Kopie der Variablen vorgenommen werden. In den meisten Fällen ist dies kein Problem, aber wenn Sie diesen Ansatz regelmäßig verwenden, sollten Sie dies im Hinterkopf behalten. Aus diesem Grund kann das Casting zu unerwarteten Problemen mit Referenzen und großen Arrays führen. Weitere Informationen finden Sie unter PHP Bug Report #50894.

  • In php wird eine ganze Zahl, die zu groß (oder zu klein) ist, um sie als Integer-Typ darzustellen, automatisch als Float (oder, falls erforderlich, als Double) dargestellt. Das bedeutet, dass das Ergebnis von ($big_int + $big_int) tatsächlich ein Float sein kann, und wenn Sie es zu einem int umwandeln, wird die resultierende Zahl Kauderwelsch sein. Also, wenn Sie Funktionen erstellen, die mit großen ganzen Zahlen arbeiten müssen, sollten Sie dies im Hinterkopf behalten und wahrscheinlich einen anderen Ansatz in Betracht ziehen.


Sorry für den langen Post, aber es ist ein Thema, das ich in der Tiefe betrachtet habe, und im Laufe der Jahre, ich habe einiges an Wissen (und Meinung) über sie gesammelt. Wenn ich es hier herausbringe, hoffe ich, dass es jemand hilfreich finden wird.

+1

Fanden Sie das wirklich hilfreich - hätte mehrmals upvolotiert, wenn es möglich wäre. – cantera

0

Nein, es ist nicht gut zu tippen, weil Sie nicht wissen, was Sie am Ende haben werden. Ich würde persönlich empfehlen, Funktionen wie intval(), floatval() usw. zu verwenden.

+1

Wie so? Entweder ist der Cast erfolgreich und Sie haben den korrekten Type-Wert, oder die Ausführung wird fehlschlagen (eine Ausnahme wird ausgelöst oder PHP bombardiert, abhängig von der PHP-Version). Gibt es etwas, das mir hier fehlt? –

+0

Ich bin auch neugierig, was Billy sagt. intval() scheint genau das gleiche zu tun wie Typ-Casting mit (int) – Anders

2

Sie können type hinting für komplexe Typen verwenden. Wenn Sie Wert + Typ vergleichen müssen, können Sie "===" zum Vergleich verwenden.

(0 === false) => results in false 
(0 == false) => results in true 

Auch Sie schreiben return (array)$result;, die keinen Sinn macht. In diesem Fall möchten Sie return array($result), wenn der Rückgabetyp ein Array sein soll.

+0

Ich bin mir der Fehler im Beispiel bewusst. Es ist die Art, wie ich es in C machen würde, aber wie du sagst würde ich es definitiv nicht in PHP machen. Es war hauptsächlich für die Konsistenz in dem Beispiel. Ich kenne Typ Hinting, aber es funktioniert nicht für primitive Typen :(Es wird nur nach der Klasse "Int" statt – Anders

2

Ich denke nicht, dass es schlecht ist, aber ich würde einen Schritt weiter gehen: Verwenden Sie type hinting für komplexe Typen, und eine Ausnahme auslösen, wenn ein einfacher Typ ist nicht einer, den Sie erwarten. Auf diese Weise machen Sie Kunden auf Kosten/Probleme mit der Besetzung aufmerksam (zB Verlust der Genauigkeit von int -> float oder float -> int).

Ihre Umwandlung in den obigen Code ist zwar irreführend - Sie sollten nur ein neues Array erstellen, das den einen Wert enthält.

Das alles gesagt, Ihr Beispiel oben wird:

public function doo($foo, $bar) { 
    if (!is_int($foo)) throw new InvalidArgumentException(); 
    if (!is_float($bar)) throw new InvalidArgumentException(); 
    $result = $bar + $foo; 
    return array($result); 
} 
+0

Ich denke, dass dies eine große Verbesserung für meine Gedanken ist. Gibt es irgendwelche Overhead zu kümmern? – Anders

+2

Ja, das Aufrufen einer Funktion in PHP ist langsam Aber als vorzeitige Optimierung ist die Wurzel allen Übels denken Sie nicht über diese kleine, unwichtige Verlangsamung, sondern denken Sie an Lesbarkeit und Wartbarkeit Ihres Codes;) – NikiC

+0

@Anders: Ja, da ist Overhead, aber ich denke nicht, dass es hier von Bedeutung ist. PHP muss den Typ prüfen, um den Cast auf jeden Fall durchführen zu können. Natürlich ist der Scheck nicht frei. –

4

Die nächste Version von PHP (vermutlich 5.4) will support scalar type hinting in arguments.

Aber abgesehen davon: Dynamische Typ Umwandlung ist wirklich nicht etwas, was Sie hassen und vermeiden sollten. Meistens wird es wie erwartet funktionieren. Und wenn dies nicht der Fall, korrigieren Sie es durch Überprüfung is_* eines Typs, durch strenge Vergleich mit, ..., ...

+2

Ich bin mir nicht sicher, ob das vereinbart wurde (dann nochmal, auf php.Interna es dauert nur 2 Personen privat zu sprechen, um eine Veröffentlichung zu arrangieren). Obwohl es einen Patch dafür gibt, bin ich mir nicht sicher, ob es in PHP vereinbart wurde. – Ross

+0

Err .. php unterstützt bereits Typhinweis (und hat für eine Weile). –

+0

@Billy: Es hat keine skalaren Hinweise unterstützt, und davon reden wir hier. – NikiC

Verwandte Themen