Konwersje PHP: (true == false)

0

Dogrzebałem się teraz co całkiem ciekawego postu na pewnym anglojęzycznym blogu.. Stupid PHP Tricks: (true == false) i myślę, że warto ten post dla niektórych przełożyć tutaj na język polski, a przy okazji niektórym da to trochę do myślenia i nauczy ostrożności ze zmiennymi w PHP. Tłumacząc mniej-więcej:

PHP jest słabo opisowym językiem: pozwala on na definiowanie zmiennych bez wcześniejszej deklaracji ich typów. Do tego wszędzie dochodzi niejawna, automatyczna i maksymalnie bezkonfliktowa konwersja różnych typów w naszych aplikacjach dzięki której możemy ze sobą porównywać liczby całkowite z ciągami znaków tekstowych, ale jeśli w ciągu znaków znajdzie się coś nietypowego… no i tutaj musisz przyłożyć większą uwagę do tego.

Jednocześnie język jest bardzo prosty i nie przykłada się zbyt dużej wagi do jego pełnego opanowania, przez co główna większość piszących w nim wyznaję zasadę „wszystko jest stringiem”, a to powoduje, że bez większych zastanowień i problemów dokonujemy operacji w stylu:

$n = "5";
echo 3 + $n;
// co wyświetli nam "8"
// W innych językach moglibyśmy dostać string "35"

i nie musimy się przejmować konwersją do odpowiedniego typu, ba, nawet ostrzeżeń o tym nie dostajemy – wszystko jest tak bystre, że dzieje się samo. Jednak bystrość ów dobroci pozwala działać również samobójczo wobec siebie i możemy bez problemu uruchomić takie kwiatki:

$a = 'string';
$b = 0;

if ( $a == true && $b == false && $a == $b )
{
    echo ( 'wszechświat się zepsuł' );
    // Tak, tekst wyżej zostanie wyświetlony
}

Przeanalizuj, a poznasz swoją wiedzę języka PHP.

Wyjaśnienie

Nie wszyscy mogą dopatrzeć się tutaj logiki – to jeszcze oznaka, że nie masz zatrutego umysły niejawnymi konwersjami w tym języku. Co dokładnie dzieje się w tym kodzie? Przeanalizujmy warunek po warunku:

  • ( 'string' == true )prawda, ponieważ każdy niepusty string jest true w przypadku porównywania do typu Boolean’owskiego
  • ( 0 == false )prawda, ponieważ 0 konwertowane do typu Boolean’owskiego jest niczym stan niski, fałsz, wyłączenie, nieprawda
  • Ostatecznie, najciekawsza część: ( 'string' == 0 )prawda, ponieważ 'string' niejawnie jest konwertowany do typu całkowitoliczbowego i wszelkie znaki ASCII reprezentujące cyfry ('1234567890') zamieniane są na odpowiedniki liczbowe, a inne są pomijane.. stąd wyrażenie ((int)'string') zwraca 0 – bo brak w nim znaków liczb

Wszystkie 3 testy są prawdziwe, przez co koniunkcja testów również jest prawdziwa – warunek został spełniony, na ekranie widzimy wszechświat się zepsuł.

Jak tego uniknąć?

Istnieje jeszcze kolejny operator do testów logicznych w PHP: ===. Sprawdza on dodatkowo zgodność typów. Jeśli jakaś funkcja zwraca następujące wartości:

  • false– w przypadku jakiegoś błędu, niepowodzenia, przerwanie operacji
  • true – brak błędów ale i brak wyniku
  • string – na przykład wydzielanie ciągu znaków, nasz główny wynik

To w takim wypadku ten operator jest niezastąpiony: w przypadku użycia „zwykłego” („luźne” porównanie – Loose comparison) porównania moglibyśmy nie wykryć, czy funkcja zwróciła oczekiwany nam string, czy tylko „wykonałam się poprawnie, ale wyniku brak”. Warto również to mieć na baczności przy ustalaniu, co nasze własne funkcje mają zwracać.

Próbowałem się jeszcze doszukać jakiegoś przykładu funkcji żywcem wyciągniętej z core języka PHP, która wymaga tego operatora.. ale nie udało mi się (..na szczęście?).

Nie wierzysz? Sprawdź sam!

Wszelkie testy i ich wyniki są również dostępne na oficjalnej stronie php.net – całkiem pokaźna tabelka, również dla niektórych funkcji typu empty(), isset() itd. – warto ją chociaż przejrzeć dla świadomości.

Przy okazji przygotowałem dla wszystkich skrypt, dzięki któremu sprawdzisz prawdziwość tego postu. Wystarczy, ze wrzucisz go do pustego pliku .php, przepuścisz przez parser i odczytasz wyniki w przeglądarce.

function br(){echo "\n";}
function pv($v){echo ' <-typ: ' . gettype($v);}

$a = 'string';
$b = 0;
$n = "5";

echo '<textarea cols="70" rows="20">';
echo '$a = \'string\'  $b = 0  $n = "5"' . "\n\n";

echo '3 + $n = ';
    echo 3 + $n;
    $var = 3 + $n;
    pv($var);

br();
echo '3 . $n = ';
    echo 3 . $n;
    $var = 3 . $n;
    pv($var);
br();

echo "\n\$a == true ? ";
    echo ($a==true)? 'PRAWDA' : 'nieprawda';
echo "\n\$b == false ? ";
    echo ($b==false)? 'PRAWDA' : 'nieprawda';
echo "\n\$a == \$b ? ";
    echo ($a==$b)? 'PRAWDA' : 'nieprawda';

br();

echo "\n\$a === true ? ";
    echo ($a===true)? 'PRAWDA' : 'nieprawda';
echo "\n\$b === false ? ";
    echo ($b===false)? 'PRAWDA' : 'nieprawda';
echo "\n\$a === \$b ? ";
    echo ($a===$b)? 'PRAWDA' : 'nieprawda';

br();
br();

echo 'if ( $a == true && $b == false && $a == $b ) ';
if ( $a == true && $b == false && $a == $b )
    echo 'PRAWDA';
else
    echo 'nieprawda'; 

echo '</textarea>';

die();

Efektem:

To tyle na dziś :-)

Tagi: , , ,

Skomentuj