Spis treści:
- O debugowaniu słów kilka
- Fazy debugowania
- Metody szukania
- Izolowanie źródła błędu
- Identyfikacja przyczyny usterki
- Usuwanie błędu
- Przykłady
O debugowaniu słów kilka
Debugowanie jest procesem, który ma na celu doprowadzić do zredukowania błędów w oprogramowaniu. W
językach wysokiego poziomu ma to miejsce przy użyciu debugera (czyli programu, który będzie kontrolował proces). Nadzorując aplikacje, która działać w czasie rzeczywistym możemy wykonywać kod linijka po linijce, obserwować kod od wybranego miejsca bądź zostawić pułapki, które uaktywnia się po wykonaniu konkretnego segmentu kodu. W PHP sprawa jest prostsza. Nie musimy kontrolować kodu linijka po linijce lecz odpowiednio reagować na komunikaty błędu. Znając znaczenie błędu w ciągu paru sekund możemy naprawić każdorazowo jeden błąd. Trzeba wspomnieć, że błędy nie musza ujawnić się odraz lecz mogą pojawić się po pewnym czasie (kwestia budowy skryptu). Może się zdarzyć tak, że programista zauważy błąd w czasie testów lecz może być też także, że jakiś użytkownik zauważy wadliwe działanie kodu. Wtedy sprawa się komplikuje nieco gdyż czas działa na nasza niekorzyść.
Fazy debugowania
- Pierwsza, najistotniejsza faza debugowania jest wykrywanie miejsca, w którym występuje defekt.
- Druga faza jest identyfikacja błędu i tego co go powoduje, jakie czynniki.
- Trzecia faza jest usuwanie usterki.
- W czwartej fazie jest weryfikacja czy naprawdę miało miejsce naprawienie błędu poprzez dokładne testy.
Metody szukania
Jak wyżej napisałem, błąd może zostać wykryty przez programistę bądź użytkownika. Jeżeli programista wykrywa bug'a to ma zadanie wykryć miejsce błędu czyli plik (linijki), nazwa funkcji bądź metody oraz poznanie wszystkich parametrów, które wpływają na nieporządne działanie danego fragmentu kodu. Dla użytkownika sprawa jest nieco prostsza. W przypadku gdzie nie ma zaimplementowanej metody informowania o błędach, użytkownik musi jedynie podać datę zdarzenia, okoliczności oraz lokalizację strony, ma której zauważył nieprawidłowość. Dodatkowo jeżeli strona wyrzuciła komunikat błędu to mile widziane będzie podanie go.
Izolowanie źródła błędu
Gdy posiadamy już informację o błędzie pierwsza czynnością jaka musimy zrobić jest wyeliminowanie wszystkich czynników, które nie wpływają bezpośrednio na powstawanie błędu. Bardzo ważne jest abyśmy już w tym momencie mieli pewność, które czynniki na pewno nie wpływają na nieprawidłowe działanie.
Identyfikacja przyczyny usterki
Odnajdywanie przyczyny usterki odbywa się poprzez obserwację danych wyjściowych i danych używanych wewnątrz danej metody. Następnie skrupulatnie krok po kroku dochodzi się do miejsca, w którym konkretna funkcja nieprawidłowo spełnia swoją rolę.
Usuwanie błędu
Gdy już wiemy gdzie leży problem musimy jedynie go usunąć np. poprzez manipuluję parametrów błędnie wykorzystanej funkcji, poprawienie łańcucha zgodności (REGEX) bądź dodanie walidacji nieprawidłowych informacji, etc.
Przykłady
Zanim przejdę do omawiania poszczególnych błędów wyprzedzę nieliczne przypadki, w których mogą nie występować komunikaty błędu. W takim przypadku trzeba sprawdzić w konfiguracji (php.ini) czy error_reporting oraz display_errors posiadają odpowiednie wartości. Więcej informacji
tutaj. Chcę także wspomnieć o tym, ze bardzo istotna rzeczą jest znajomość angielskiego. To dzięki niemu szybko dotrzemy do wniosku co jest źle. Wystarczy zobaczyć na pierwszy przykład i wynieść wnioski ze słowa unexpected czyli niespodziewany. Ostatnią rzeczą jaka chce dodać tutaj jest fakt, że zanim zapytamy powinniśmy użyć
wszystkowiedzącego wujka google!
Bill Gates napisał(a):
Jeżeli nie można czegoś znaleźć w Google, to to po prostu nie istnieje
unexpected T_ECHO badź T_STRING, deklarowanie zmiennych
Kod:<?php
$zmienna = 'text'
echo $zmienna;
?>
Oczywiście w przykładzie powyżej brakuję średnika po stworzeniu zmiennej. Aby pozbyć się tego błędu należy jedynie go
dokleić.
Kod:<?php
$zmienna = 'text';
echo $zmienna;
?>
T_STRING wystąpi wtedy gdy użyjemy funkcji
print_r() zamiast
echo().
Nieprawidłowe łączenie ciągów
Dość często popełniany błąd przez nowicjuszy. Korzystając z okazji wyjaśnię różnicę miedzy cudzysłowem pojedynczym, a podwójnym. W pojedynczym cudzysłowu $zmienna pozostanie jedynie zestawem znaków, a w podwójnym zostanie zinterpretowana i do tworzonego stringu zostanie wrzucona wartość $zmienne. Drugą różnicą jest to, że w cudzysłowu podwójnym znaki cytowane ulegają interpretacji. Przykłady poniżej.
Kod:<?php
$dodatkowa_zmienna = 'php';
$ciag_znakow = 'test \r\n test $dodatkowa_zmienna';
echo $ciag_znakow;
?>
Wynikiem tego kodu powinno być:
Kod:test \r\n test $dodatkowa_zmienna
Teraz przykład z podwójnym cudzysłowem.
Kod:<?php
$dodatkowa_zmienna = 'php';
$ciag_znakow = "test \r\n test $dodatkowa_zmienna";
echo $ciag_znakow;
?>
Wynikiem powyższego kodu powinno być:
Kod:test
test php
Znak podwójnego cudzysłowa wewnatrz " i "
Kod:<?php
$ciag_znakow = "test znak " i mamy problem";
echo $ciag_znakow;
?>
Jak widzimy powyżej składnia "nieprawidłowo" zaczęła nabierać kolorów. Oto solucja jak poprawić powyższa deklaracje.
Kod:<?php
$ciag_znakow = "test znak \" i już nie ma problemu";
echo $ciag_znakow;
?>
Analogicznie ta sama sytuacja dotyczy '. Mieszanie ' i " jest dość częstym problemem spotykanym na forach. Szczególnie często występuję w
zapytaniach SQL.
Kod:<?php
$sql = 'SELECT obiekt FROM tabela WHERE family = 'Niespodzianka'';
echo $sql;
?>
Jeżeli zbudowane przez Ciebie zapytanie wyrzuca błąd bądź nie zwraca wyników wyrzuć je za pomocą
echo() podobnej funkcji bądź użyj funkcji
mysql_errno() (w przypadku używania MySQL). W powyższym kodzie nieprawidłowo połączono ze sobą '. Oto optymalny sposób sformułowania zapytania:
Kod:<?php
$sql = "SELECT obiekt FROM tabela WHERE family = 'Niespodzianka'";
echo $sql;
?>
Jeżeli uprzemy sie przy stosowaniu pojedynczego cudzysłowu to tylko musimy odpowiednio używać znaku \ przed '
Kod:<?php
$sql = 'SELECT obiekt FROM tabela WHERE family = \'Niespodzianka\'';
echo $sql;
?>
Kolejnym błędem jest używanie jakiś niespotykanych wzorców przy łączeniu ciągów i zmiennych.
Kod:<?php
$extra = 'extra';
$zmienna = 'test '$extra' test '$extra;
echo $zmienna;
?>
W powyższym przykładzie zapomniano oczywiście o kropce miedzy zmienną a ciągiem. Poniżej poprawny sposób:
Kod:<?php
$extra = 'extra';
$zmienna = 'test '.$extra.' test '.$extra;
echo $zmienna;
?>
Powyższy kod można także przedstawić w nieco innej formie.
Kod: <?php
$extra = 'extra';
$zmienna = "test $extra test $extra";
echo $zmienna;
?>
Czasami zdarza się, że musimy dodać cos do zmiennej. W takim przypadku robimy to w ten sposób:
Kod:<?php
$zmienna = 'test';
$zmienna .= ' extra';
echo $zmienna;
?>
Dyrektywa global w funkcjach
Czasami zdarza się, że musimy mieć dostęp do zmiennej z zewnątrz. Są dwie metody. Pierwsza to przekazywanie danych przez parametry funkcji. Druga metoda oznacza zastosowanie dyrektywy global. Oto prosty przykład jak to działa.
Kod:<?php
$zmienna = 'text';
function test()
{
global $zmienna;
return $zmienna.' cos extra';
}
echo test();
?>
Jeżeli usuniecie global wtedy wyświetli się tylko
cos extra
failed to open stream
Najczęstsza wina takiego stanu rzeczy jest nieprawidłowa ścieżka, zła lokalizacja do zdalnego pliku bądź nie odpowiadający serwer. Na początku omówimy kwestie związana z otwieraniem nieistniejącego pliku za pomocą funkcji
fopen().
Kod:<?php
$uchwyt = fopen('./plik.txt', 'r');
?>
Powyższy kod wygeneruję błąd
No such file or directory jeżeli plik nie istnieję. Aby zapobiec takiemu zdarzeniu trzeba dodatkowo użyć funkcji
file_exists().
Kod:<?php
$plik = './plik.txt';
if(!file_exists($plik))
{
echo 'Plik nie istnieje';
}
else
{
$uchwyt = fopen($plik, 'r');
}
?>
Gdyby plik nie posiadał odpowiedniego
chmodu to dodatkowo dostalibyśmy komunikat
Permission denied. Dlatego też trzeba się dobrze zastanowić w jaki sposób będziemy operować na plikach.
fsocketopen() i unable to connect
Mamy sobie taki kod, który doprowadzi do błędu ze względu na to, że próbujemy połączyć się z nieistniejącym serwisem.
Kod:<?php
$uchwyt = fsockopen("www.example.com", 88, $errno, $errstr, 2);
if(!$uchwyt)
{
echo "$errstr ($errno)";
}
else
{
echo 'dalsze operacje';
}
?>
Przypuśćmy iż host i port są prawidłowe to jednak przy przekroczeniu limitu czasowego funkcja także wywali błąd. W tym przypadku mamy dwie opcje. Jedna z nich to zmiana poziomu raportowania błędów w php.ini. Druga opcja to proste stłumienie błędów za pomocą @ przed nazwa funkcji.
headers already sent by
Mój ulubiony błąd. Najczęściej spotykany na wszelkiego rodzaju forach. Taki komunikat mówi nam o tym, że przed wysłaniem nagłówka (np. rozpoczęciem sesji, wysłaniem ciastka) zostało już coś wysłane do przeglądarki. Aby zapobiec temu zjawisku trzeba usunąć wszystkie znaki wędrujące do browsera przed operacjami, które wysyłają dodatkowe nagłówki do przeglądarki bądź zastosowanie buforu, patrz
ob_start() i
ob_end_flush(). Powyższy błąd dość szczegółowo opisałem w artykule pod tytułem
[PHP] headers already sent by.
Błędy logiczne
Czasami zdarza nam się, że popełnimy błąd logiczny tj. taki kiedy podamy nieprawidłowe warunki dla instrukcji warunkowej. Przykład poniżej:
Kod:<?php
$x = 17;
$y = 22;
if($x < 17 && $y > 22)
{
/* ten warunek nigdy nie zostanie spełniony */
}
?>
Wyjątki w składni
Pamiętajmy aby nie zostawiać przez przypadek ; po pętli. Czasami zdarza się, że edytor sam dokleja. Poniższy przykład wyrzuci tylko 5.
Kod:<?php
for($h = 0; $h < 5; ++$h);
{
echo $h.'<br />';
}
?>
Dozwolone znaki w nazwach zmiennych
Pamiętajmy, że w nazwach zmiennych i tablic nie możemy używać niedozwolonych znaków. Wolno używać a-z, A-Z, 0-9 (z wyjątkiem początku nazwy) oraz _ dodatkowo możemy użyć znaków z ASCII od 127 do 255.
Liczymy od 0 a nie od 1
Pamiętajmy, że w językach programowania indeksy tablic zaczynają się od 0 a nie od 1.
Brakujące klamerki w instrukcjach warunkowych
Czasami zdarzy się ze gdzieś brakuje klamerki. Gdy kod jest mocno rozbudowany to znalezienie takiego błędu doprowadzi do straty czasu. Dlatego tez warto stosować wcięcia w kodzie i starać się aby kod był jak najbardziej czytelny.
Kod:<?php
$zmienna = 'test';
if('test' != 'test')
/* ucinamy { */
echo 'cos sie dzieje';
}
$zmienna2 = 'test';
?>
Powyższy kod wskaże nam 7 linię. W tym przypadku detekcja będzie prosta. Jednak co się stanie gdy utniemy klamerkę końcową?
Kod: <?php
$zmienna = 'test';
if('test' != 'test')
{
echo 'cos sie dzieje';
/* ucinamy } */
$zmienna2 = 'test';
?>
Wskazało nam błąd w linii 11. Zabawne gdyż ten kod posiada tylko 10 linii. Właśnie w takich przypadkach mamy przysłowiowy pasztet szczególnie kiedy plik liczy parę tysięcy linijek. Dlatego ważne jest robienie częstych kopii zapasowych przez edytor. Potem unikniemy takich problemów jeżeli nagle okaże się ze gdzieś popełniliśmy błąd i nie potrafimy go zlokalizować. Możemy oczywiście próbować znaleźć błąd przez echo. Przed błędem dana treść zostanie wyświetlona lecz po błędzie już nie.
Perfekcja
Testy, testy i jeszcze raz testy. Bez nich szybko wejdziemy na minę. Dlatego też ważne jest abyśmy starali się na bieżąco testować kod w rożnych warunkach. Ważne jest aby samemu szukać rozwiązania, a nie czekać na to, że ktoś z forum znajdzie je za Ciebie!
Własna obsługa błędów
Aby tego dokonać tworzymy własna funkcje, a następnie używamy funkcji
set_error_handler() aby nakierować na funkcje obsługująca własne błędy.
Kod:<?php
function wlasny_blad($errno, $errstr)
{
echo '<b>Blad:</b> ['.$errno.'] '.$errstr.'<br />Blad zalogowano';
exit;
/* tutaj można dodać funkcje logująca takie przypadki */
}
set_error_handler('wlasny_blad');
$uchwyt = fopen('cokolwiek', 'r');
?>
Wyjątki
Poniższy kod przedstawia metode zastosowania wyjątkow w PHP.
Kod:<?php
function dzielenie($a)
{
if($a !== 0)
{
return 1/$a;
}
else
{
throw new Exception('Dzielenie przez zero.');
}
}
try
{
echo dzielenie(1).'<br />';
echo dzielenie(0).'<br />';
}
catch (Exception $e)
{
echo 'Złapano wyjątek: '.$e -> getMessage();
}
?>
Komentarze
Publikowane komentarze są prywatnymi opiniami użytkowników serwisu. Serwis nie ponosi odpowiedzialności za treść opinii. W trosce o zachowanie poziomu dyskusji wszystkie komentarze podlegają akceptacji przed ich publikacją dlatego proszę cierpliwie czekać aż komentarz zostanie opublikowany.
Dodaj komentarz
Zezwolono używać:
BBCode
Zabroniono używać:
znaczników HTML
CapaciousCore
Być może ta funkcja może się Wam także przydać debug_backtrace().