arrow Blog
rss

Cum facem debugging în PHP

October 21st, 2011

În ceea ce privește debugging-ul și profiling-ul, PHP oferă un suport standard nesemnificativ, golul fiind umplut însă de o serie de extensii și instrumente precum:

- XDebug – o extensie PHP care oferă facilități precum profiling, code coverage, remote debugging, afișarea stack trace-urilor, afișarea îmbunătățită a rezultatelor produse de var_dump etc.;

- APD – Advanced PHP Debugger – este tot o extensie de PHP care oferă un API pentru debugging;

- DBG – software disponibil atât gratuit, cât și cu plată;

- FirePHP – permite tipărirea mesajelor în consola Firebug, folosind apeluri de funcții executate în scripturile PHP;

- Zend IDE, care vine cu propriul său debugger etc.

Primii pași

Atunci când începem un proiect este bine să avem un mediu de dezvoltare configurat corespunzător, astfel încât să putem fi notificați din timp de eventualele probleme apărute, oricât de nesemnificative ar putea fi. Astfel, când dezvoltăm o aplicație în PHP trebuie să ne asigurăm ca nivelul de raportare a erorilor este cel mai ridicat posibil și că, evident, PHP-ul este configurat să afișeze erorile. Setările necesare ar putea fi făcute dinamic la începutul unui script, după cum urmează:

ini_set('display_errors', 'On');
error_reporting(E_ALL);

Merită menționat faptul că, începând cu PHP5, este disponibilă constanta E_STRICT, care, printre altele, activează emiterea avertizărilor referitoare la metodele deprecated folosite în programul vostru. Astfel, linia 2 ar deveni:

error_reporting(E_ALL | E_STRICT)

Suportul standard existent în PHP – limitat

După cum am spus și mai devreme, suportul standard pentru debugging în PHP este insuficient, fiindu-ne puse la dispoziție doar câteva funcții, cum ar fi: var_dump, print_r, sau debug_backtrace. Dintre ele, probabil cea mai utilă (probabil după var_dump :D ) ar fi debug_backtrace care, după cum afirmă și numele ei, generează o listă detaliată cu toate apelurile ce au condus la execuția funcției sau liniei unde a fost utilizată, returnând această listă ca un array. Iată și un exemplu:

function error_handler($errNo, $errStr) {
    print '<pre>';
    var_dump(debug_backtrace());
    print '</pre>';
}

set_error_handler('error_handler');
trigger_error('An error occured', E_USER_ERROR);

Am ilustrat aici un posibil scenariu de utilizare: obținerea backtrace-ului într-un handler global de erori, pentru a investiga condițiile producerii erorii respective. O puteți folosi însă în orice funcție în care doriți să aflați care este contextul în care aceasta este executată.

Componente externe pe care le putem folosi

Pe lângă capabilitățile standard, avem la dispoziție o suită întreagă de extensii și de tool-uri externe de debugging, o parte din ele amintite ceva mai sus. În continuare voi vorbi însă numai de una dintre ele: XDebug.

XDebug

XDebug este o extensie de PHP care facilitează depanarea scripturilor oferind funcționalități precum:

- stacktrace-uri cu un nivel configurabil de detaliere, furnizate automat la apariția unei erori;

- afișarea îmbunătățită a conținutului variabilelor;

- code coverage – evidențierea liniilor și secvențelor de cod ce au fost efectiv rulate într-un script;

- profiling – măsurarea performanțelor înregistrate de script ca ajutor în determinarea punctelor lente conținute de acesta;

- remote debugging – XDebug furnizează o interfață prin care programele externe de debugging pot interacționa cu scripturile PHP.

Stacktrace-urile și configurarea detaliilor oferite de acestea

Atunci când în script va fi declanșată o eroare, fie ea de către interpretor, fie de către programator (folosind trigger_error), XDebug este configurat să afișeze implicit un stacktrace asociat acelei erori.

function trigger_user_error() {
    trigger_error('An error occured', E_USER_ERROR);
}
trigger_user_error();

Această secvență de cod va produce următorul stacktrace:

Implicit, sunt prezentate următoarele informații:

- Momentul de timp la care a început execuția funcției;

- Câtă memorie era folosită în momentul respectiv;

- Numele funcției executate;

- Locul (fișierul și linia) în care a fost executată funcția.

Dar, uneori avem nevoie de ceva mai multe informații decât atât, cum ar fi informații despre variabilele transmise ca parametru funcțiilor afișate în stacktrace. Pentru a obține aceste informații, trebuie să activăm colectarea parametrilor în XDebug, atribuind valoarea dorită directivei de configurare xdebug.collect_params. Spre exemplu, atribuirea valorii 4 are drept rezultat afișarea numelui variabilei și a conținutului acesteia:

ini_set('xdebug.collect_params', '4');

Pe lângă această facilitate, XDebug mai permite și afișarea conținutului variabilelor superglobale, sau a celor locale. Pentru mai multe detalii, accesați pagina de documentație referitoare la stacktrace-uri.

Totuși, dacă dintr-un oarecare motiv doriți să dezactivați temporar afișarea stacktrace-urilor, puteți folosi funcția xdebug_disable() și, pentru a reactiva această funcționalitate, xdebug_enable().

Afișarea îmbunătățită a conținutului variabilelor

XDebug înlocuiește funcția var_dump existentă în PHP, varianta oferită în schimb producând un output preformatat (nu mai este deci nevoie să folosim tag-ul <pre> pentru a putea citi rezultatul funcției), în care tipurile diferite de date sunt colorate diferit, permițând totodată configurarea nivelului maxim de imbricare, precum și a porțiunii afișabile dintr-un șir de caractere.

ini_set('xdebug.var_display_max_children', 5);
ini_set('xdebug.var_display_max_data', 5);
ini_set('xdebug.var_display_max_depth', 1);

$object = new stdClass;
$object->self = $object;

$array = array(
	'first' => 1,
	'second' => 2,
	'third' => 3,
	'fourth' => 'Lorem ipsum dolor sit amet (...)',
	'fifth' => $object,
	'sixth' => 'Not shown'
);
var_dump($array);

Secvența de cod de mai sus produce următorul rezultat:

Astfel:

- numărul de elemente afișate dintr-un array poate fi configurat cu ajutorul directivei xdebug.var_display_max_children;

- numărul de caractere afișate dintr-un string poate fi configurat cu ajutorul directivei xdebug.var_display_max_data;

- nivelul maxim de niveluri imbricate afișate (în cazul array-urilor și al obiectelor) poate fi configurat cu ajutorul directivei xdebug.var_display_max_depth.

Code coverage

Activarea code coverage-ului permite detectarea liniilor dintr-un script, precum și fișierele corespunzătoare, care sunt efectiv executate la rularea unui program. Acest lucru este util atunci când doriți să aflați cât de eficiente sunt testele pe care le-ați scris, întrucât vă arată practic ce zone de cod au rămas neacoperite de test case-uri.

ini_set('xdebug.coverage_enable', '1');

xdebug_start_code_coverage();

function execute($value) {
	if ($value % 2 == 0) {
		return $value + 3;
	} else {
		return $value + 2;
	}
}

$values = array( 2, 4, 6, 8, 10 );
foreach ($values as $value) {
	$value = execute($value);
}

$coverage = xdebug_get_code_coverage();

print '<pre>';
foreach ($coverage as $file => $execLines) {
	$fileContents = file($file);
	$lines = array_keys($execLines);
	print '<b>' . $file . '</b>' . PHP_EOL;
	foreach ($fileContents as $idx => $line) {
		if (in_array($idx + 1, $lines)) {
			print '<span style="background-color:red; color:white;">'
				. htmlentities($line) .
					'</span>';
		} else {
			print htmlentities($line);
		}
	}
}
print '</pre>';

Exemplul de față ilustrează următoarele operații:

- activarea code coverage-ului (acesta este dezactivat implicit, din motive ce țin de performanță);

- începerea colectării informațiilor legate de code-coverage, apelând xdebug_start_code_coverage();

- citirea acestor informații într-un array, folosind xdebug_get_code_coverage();

- folosirea datelor obținute pentru a evidenția liniile executate (liniile 20 – 35).

Remote debugging

XDebug oferă o interfață prin intermediul căreia clienții de debugging pot interacționa cu scripturile PHP. Pentru a realiza acest lucru, XDebug pune la dispoziție două protocoale:

- vechiul protocol GDB, folosit din linia de comandă;

- DBGp, suportat începând cu XDebug 2, folosit și în exemplele de mai jos.

În ceea ce privește clienții de debugging, oferta este foarte variată, din care am ales plug-in-ul de Notepad++, ce poate fi descărcat gratuit de aici. Pentru a-l instala, pur și simplu copiați fișierul dbgpPlugin.dll în folderul plugins din locul unde ați instalat Notepad++, după care reporniți editorul.

Pentru a putea iniția o sesiune de debugging trebuie mai întâi să configurați XDebug:

- activarea suportului pentru remote debugging se face atribuind valoarea 1 directivei xdebug.remote_enable;

- adresa unde rulează clientul de debugging și portul pe care ascultă acesta sunt specificate ca valori ale directivelor xdebug.remote_host, respectiv xdebug.remote_port;

- modul de debugging este stabilit folosind directiva xdebug.remote_mode; valorile posibile sunt ”req” (implicit, sesiunea de debugging este inițiată odată cu pornirea scriptului) sau ”jit” (sesiunea de debugging este inițiată la apariția unei erori).

Aceste configurări vor fi salvate în php.ini, sub grupul XDebug:

[XDebug]
xdebug.remote_enable = 1
xdebug.remote_host = 127.0.0.1
xdebug.remote_port = 9000
xdebug.remote_mode = req

Pentru ca aceste modificări să aibă efect, trebuie ca server-ul web să fie repornit. Odată configurat XDebug, puteți porni consola de debugging din Notepad++:

Interfața de debugging oferă aproape toate controalele obișnuite pentru o astfel de consolă: stabilirea breakpoint-urilor, step-in, step-out, step-over, watch etc. După ce ați stabilit breakpoint-urile nu vă ramâne decât să accesați din browser script-ul pe care doriți să îl depanați adăugand la URL parametrul XDEBUG_SESSION_START=nume_sesiune, pentru a activa debugger-ul.

Pentru a putea vedea contextele local (variabilele aflate în scriptul / metoda / funcția curentă) și global (variabilele superglobale$_SERVER, $_ENV etc.) actualizate la fiecare step, trebuie să accesați interfața de configurare a plugin-ului: Plugins / DBGp / Config. Aici va trebui să bifați următoarele opțiuni:

- Refresh local context on every step – pentru actualizarea contextului local;

- Refresh global context on every step – pentru actualizarea contextului global.

debug_backtrace
Taguri: , ,
317 afisari

Comenteaza

*