Winuser's Blog

11 grudnia 2011

część II : Logicznie błędny kod – PHP

Filed under: PHP,Programowanie — winuser @ 15:11
Tags: ,

Wpis jest kontynuacją poprzedniego postu o błędach logicznych w kodzie programu. Ponieważ ostatnio zmuszony byłem do ostrego siedzenia nad kodem w języku PHP, przez pewnego klienta, dlatego wpis będzie właśnie traktował o tym języku. Okazuje się, że w PHP niewiele trzeba aby pisać kod logicznie błędny, jednocześnie trudny do poprawienia, szczególnie gdy projekt jest większy niż te kilkaset linijek.
Na początek klasyka gatunku pod tytułem „błąd z pętlą” :

while (cos_tam);
{
 // jakis kod
 int a = 1 + 2;
}

Klasyka tym razem dla operatora równości :

if ($nextRowID = $rowID)
 break;

Jak widać, tym razem programista tak się śpieszył, że napisał tylko jeden znak „=”, w wyniku czego wyszedł mu operator przypisania. Efektem tego będzie zawsze true, czyli warunek jest bezsensowny.

Programiści języków z rodzaju C/C++ automatycznie dodają znak średnika na końcu wiersza. Zdarza im się to nawet poza kodem.
Kolejny przykład, jeden z moich faworytów. Przykład typowy dla języka PHP w którym zmienna tworzona jest w miejscu pierwszego użycia :

$installler[$row['ID']] = $row['value'];
// kilkaset linii dalej lub w innym pliku
echo $installer['1'];

Dla tych którzy jeszcze nie zauważyli co z powyższym kodem jest źle, już śpieszę z wytłumaczeniem. Chodzi o potrójną literkę `l` w nazwie zmiennej. W wyniku tej pomyłki interpreter PHP utworzy dwie zmienne, jedną $installler oraz drugą $installer. Oczywiście w $installer nie będzie tego co chcieliśmy osiągnąć w linii numer 1 🙂
Czas na przejście do kolejnego przykładu. Jest on również typowy dla PHP. Typowy, ponieważ w języku C kompilator rzuciłby warning „not all paths return a value”.

function Worker($arg_1)
{
 if ($arg_1 < 1024)
 return false;
}

function Another()
{
 // gdzieś w innym, odległym miejscu..
 $result = Worker(300);
 if ($result == true)
 {
 // nigdy się nie wywoła..
 }
}

Ten błąd akurat można w całkiem prosty sposób wykryć, wystarczy zajrzeć w ciało funkcji „Worker”.
Kolejny przykład dotyczy zapytania MySQL. Z bazami danych programista PHP ma doczynienia praktycznie ciągle, więc przykład również się nadaje.

$ret = $db->query("SELECT * FROM `usr` WHERE `uid` = '$uid' AND `field` = $var;");
if ($ret == false || $ret->num_rows == 0)
{
 echo "error";
}

Co będzie wynikiem tego zapytania gdy zmienna $var nie będzie miała wartości nadanej ? Zapytanie będzie błędne. Rozwiązaniem jest branie wszystkiego co możliwe, w cudzysłowy pojedyncze, czyli ostatni kawałek zapytania wyglądałby :

AND `field` = '$var';

Jak widać PHP jest bardzo podatny na błędy w logice kodu. Dzieje się tak w dużej mierze z tego powodu że nie ma on określonych na sztywno typów danych, czyli zmienna która przed chwilą przechowywała tekst, za chwilę może już przechowywać liczbę typu double albo zmienną typu bool. Ta właściwość może być błogosławieństwem albo zmorą. Być może ja osobiście mam takie problemy podczas programowania w PHP, ponieważ jestem dość silnie związany z C / C++ w których to językach jak wiadomo panuje dość silna typizacja zmiennych. Dodatkowym powodem problemów jest to że interpreter PHP nie zwraca uwagi czy funkcja zwraca jakąś wartość, czy może zwraca tylko czasami, ponieważ nie istnieje prototyp funkcji do której kod musi być dopasowany (czyli że funkcja zwraca typ bool i koniec). Na dłuższą metę programując w PHP chyba być zwariował 😉

5 grudnia 2011

część I : Dlaczego ten kod źle działa ? (C++)

Filed under: C++,Programowanie — winuser @ 3:09
Tags: , ,

Tak to już jest w życiu kodera, że często popełnia głupie błędy. Często te błędy są banalne, kuriozalne, groteskowe i jak chcecie je jeszcze nazywajcie. Sam już nieraz się z tym spotkałem (we własnym kodzie). Było to jakiś miesiąc temu kiedy siedziałem nad pewnym błędem a debugowanie aplikacji wcale nie dawało tak jasnych odpowiedzi na pytania jak wtedy tego chciałem. Zdarza się, że w człowiek jest tak „zagoniony” że poprostu nie zauważa jak oto walnął pięknego byka w kodzie (dla osoby z zewnątrz może być on widzialny dosłownie na pierwszy rzut oka).

Pierwszym przykładem, jest operator != bardzo często zresztą stosowany w przeróżnych pętlach, if`ach itd. Dlaczego może on być zagrożeniem ? Popatrzmy na przykład w kodzie :

void Fun()
{
    static unsigned int ret = 0;
    char* buf = NULL;

    ret = klasa->recv()

    if(ret != 0)
    {
        buf = new char[ret];
        // tutaj cos robimy z buf
        delete [] buf;
    }
}

Przykład trywialny, pod pewnym względem z nie wiadomo skąd wzięty. Załóżmy że metoda recv z klasy klasa zwróci nam -1 (dajmy na to kod błędu). Samo sprawdzenie czy nie jest równe 0 nie daje nam w tym przypadku bezpieczeństwa. Zostanie utworzona (złe słowo, będzie próba utworzenia) tablicy z ujemnym rozmiarem, co oczywiscie zaowocuje access violation. Sam na czymś podobnym się złapałem i postanowiłem troszkę bardziej myśleć przy warunkach.

Kolejny ładny przykład, znów groteskowy. Łatwo o niego gdy już jesteśmy mocno zmęczeni :

void Fun(int elem_id)
{
    for(int current_id = 0;
    current_id < elem_id; ++elem_id)
    {
        cout << current_id;
    }
}

Istnieje wiele wersji tej pętli („dlaczego te gówno nie chce działać ?!”), np

void Fun(int elem_id)
{
    for(int current_id = 0; current_id < elem_id; ++current_id)
    {
        klasa->Operacja(--current_id);
    }
}

Może ci się to wydawać śmieszne (albo i nie jeśli sam przeżywasz często to co ja) ale po 20 h pisania niemal non stop naprawde trudno to dostrzec. Po prostu wiesz, że produkujesz shit (bo jak inaczej nazwać podobne konstrukcje), męczysz się, podnosisz ciśnienie 6 kawą i nie możesz dostrzec tego głupiego błędu ! Ale to już w sumie temat na innego posta.

W najbliższym czasie wrzucę część drugą wpisu, tym razem będzie ona dotyczyć języka PHP.