Ankündigung

Einklappen
Keine Ankündigung bisher.

Fallstricke bei typeschwachen Stringvergleichen

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Fallstricke bei typeschwachen Stringvergleichen

    Es ist bei Stringvergleichen gar nicht so selten zu finden, statt strcmp zu benutzen dies mit einem einfachen Vergleich (==) zu erledigen,
    also statt
    PHP-Code:
    $product 'Milch';
    if(
    strcmp($product,"Milch") == 0).. 
    einfach
    PHP-Code:
    if($product == "Milch").. 
    zu schreiben.

    Klappt auch in den meisten Fällen, aber leider nicht immer. Weniger bekannt ist:
    PHP prüft den vor den einfachen (typeschwachen) Vergleich beide Stringinhalte, ob diese einen numerischen Inhalt haben.
    Sind beide Stringinhalte numerisch, wird auch ein numerischer Vergleich durchgeführt.
    So auch wenn eine Seite ein numerischer Datentyp ist, was allgemein bekannt ist.

    Beispiele typeschwacher Stringvergleiche
    PHP-Code:
    $myString '01200';
    if(
    $myString == '0001200') echo "'0001200':true ,";
    if(
    $myString == '12e2') echo "'12e2':true ,";
    if(
    $myString == '0x4b0') echo "'0x4b0':true ,";
    if(
    $myString == ("\r\n\t ".'1200')) echo "'CRLF 1200':true ,";
    if(
    $myString == '1200.0000000000000000028') echo "'1200.0000000000000000028':true ,"//32Bit System
    //Ausgabe
    //'0001200':true ,'12e2':true ,'0x4b0':true ,'CRLF 1200':true ,'1200.0000000000000000028':true , 
    Wie die Beispiele zeigen, werden auch Hexadezimalausdrücke als numerisch angesehen und führende Steuerzeichen werden auch großzügig ignoriert.
    Eine führende 0 führt dagegen nicht zu einer Interpretation als Oktalzahl.

    Diese Stolperfallen lauern versteckt auch in anderen Konstrukten, wie z.B. switch oder usort.
    Dazu die folgenden Beispiele.

    Beispiel switch
    PHP-Code:
    $selector '07';
    switch (
    $selector) {
      case 
    '7e0' : echo 'Fall 1';
                 break;
      case 
    '07': echo 'Fall 2';
                 break;
    }
    //Ausgabe: Fall 1 
    Hier wird numerisch verglichen, daher tritt Fall 1 ein, obwohl '7e0' !== '07' ist.
    Im PHP-Manual zu switch ist zwar der Hinweis zu lesen daß switch typeschwache Vergleiche durchführt, ignoriert aber die Probleme durch "Beispiel #2 switch gestattet den Vergleich mit Strings".

    Die folgende switch-Variante nutzt den strengen Vergleich, ist aber schlecht lesbar:
    PHP-Code:
    $selector '07';
    switch (
    true) {
      case 
    $selector==='7e0' : echo 'Fall 1';
                 break;
      case 
    $selector==='07': echo 'Fall 2';
                 break;
    }
    //Ausgabe: Fall 2 
    Ich persönlich nutze switch nur ungerne und lieber if/elseif/else Konstrukte.

    Beispiel usort
    PHP-Code:
    function cmp($a$b)
    {
    //ungeeignet für strings
        
    if ($a == $b) {
            return 
    0;
        }
        return (
    $a $b) ? -1;
    }
    function 
    cmpStr($a$b)
    {
        return 
    strcmp($a$b);
    }

    $a1 $a2 $a3 = array('0xa','0x1','011','0af',);
    sort($a1SORT_STRING);
    var_dump($a1);
    echo 
    "<br>";

    usort($a2"cmpStr");
    var_dump($a2);
    echo 
    "<br>";

    usort($a3"cmp");
    var_dump($a3);

    /*
    Ausgabe:
    array(4) { [0]=> string(3) "011" [1]=> string(3) "0af" [2]=> string(3) "0x1" [3]=> string(3) "0xa" }
    array(4) { [0]=> string(3) "011" [1]=> string(3) "0af" [2]=> string(3) "0x1" [3]=> string(3) "0xa" }
    array(4) { [0]=> string(3) "0af" [1]=> string(3) "0x1" [2]=> string(3) "0xa" [3]=> string(3) "011" } 
    */ 
    Das Beispiel zeigt deutlich, daß usort bei Verwendung der Vergleichsfunktion cmp mit dem typschwachen Vergleich nicht das gewünschte Resultat bringt.
    sort und usort mit cmpStr, welche strcmp benutzt, arbeiten richtig.

    Fazit:
    Für den sicheren Stringvergleich auf Gleich/Ungleich kann nur der strenge Vergleich (===/!==) empfohlen werden.
    Wird in den obigen ersten Beispielcode konsequent === benutzt, bleibt die Ausgabe leer.
    Für einen größer/kleiner Vergleich von Strings bleibt nur strcmp zu nehmen.

    LG jspit

    PS: Mich hat dies mal viel Zeit bei einer Fehlersuche gekostet, vielleicht erspart dieser Beitrag dies dem Einen oder Anderen.

  • #2
    Ich war so frei (leicht abgeändert): http://php-de.github.io/jumpto/stringvergleiche/

    Danke!

    PS: Wie üblich.. Wenns was dazu gibt, du weißt wo du mich findest
    The string "()()" is not palindrom but the String "())(" is.

    Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
    PHP.de Wissenssammlung | Kein Support per PN

    Kommentar

    Lädt...
    X