• доступно о веб-разработке
01.12.2012 PHP, Инструменты, Технологии, Уроки

Отладка кода в PHP.


Вы уже научились писать код на PHP, знаете MySQL и это хорошо, но как в любой программе есть ошибки, так и в вашем коде обязательно будут ошибки, от этого никуда не деться. Иногда это явные ошибки, к примеру забыли поставить в конце строки «;», иногда это не явные, когда все работает, но результат явно не тот который вы ожидали. Что тогда делать?

Об этом, мы сейчас и поговорим.

1. print_r() || var_dump() || var_export()

Представим что у вас есть массив данных и вам надо его посмотреть, что же лежит внутри этого замечательного массива…

Самый быстрый и простой способ
[sourcecode language=»php»]
print_r($array);
[/sourcecode]
И на странице, мы сможем увидеть внутренности нашего массива.
[sourcecode language=»php»]
Array ( [0] => a [q] => 22 [s] => 142 [dump] => Array ( [zero] => 5 [dump] => foo ) );
[/sourcecode]
Как видим, функция print_r() выдала нам не только данные, но и вывела структуру массива. Что очень может помочь.

Данные можно передать в переменную, просто в качестве второго аргумента передайте TRUE.
[sourcecode language=»php»]
$var = print_r($array, TRUE);
print $var;[/sourcecode]

Это удобно, если нельзя делать вывод на страницу напрямую. Или для сохранения данных в базу.

Но в данном выводе, не совсем понятно, [q] => 22, это число или это текст? А если надо вывести сразу 2 или более, переменных или массивов? Можно конечно использовать два и более раз функцию print_r(), и в этом даже нет ничего плохого. Но было бы удобней, сразу перечислить все переменные или массивы в одной функции и получить желаемый результат.

Тут на помощь нам приходит var_dump()
[sourcecode language=»php»]var_dump($array);[/sourcecode]
выдаст
[sourcecode language=»php»]
array(4) { [0]=> string(1) "a" ["q"]=> int(22) ["s"]=> string(3) "142" ["dump"]=> array(2) { ["zero"]=> string(1) "5" ["dump"]=> string(3) "foo" } }
[/sourcecode]
Как видно, уже больше информации выдало нам. Теперь нам известно, где текст, а где число.
И в таких случаях
[sourcecode language=»php»]
if(is_int($array['s'])){
//некое действие
}
[/sourcecode]
Мы сразу поймем, почему не работает данное условие.

Также, можно указать сразу два массива или более, в данной функции и получить вывод сразу всех массивов.

Создадим еще один массив,
[sourcecode language=»php»]
$clone_array = array('1'=>'b','boo'=>'541', 'z'=>'44', 'relese'=> array ('one'=>'11', 'dump'=>'zoo'));
[/sourcecode]
И получаем вывод.
[sourcecode language=»php»]
array(4) { [0]=> string(1) "a" ["q"]=> int(22) ["s"]=> string(3) "142" ["dump"]=> array(2) { ["zero"]=> string(1) "5" ["dump"]=> string(3) "foo" } } array(4) { [1]=> string(1) "b" ["boo"]=> string(3) "541" ["z"]=> string(2) "44" ["relese"]=> array(2) { ["one"]=> string(2) "11" ["dump"]=> string(3) "zoo" } }
[/sourcecode]
Помимо указания где int, а где string, var_dump(), указывает и другие типы данных, float, bool, resource, array, object.

Третья функция var_export(), это почти тоже самое, что и var_dump(), только возвращает представление является правильным РНР-кодом.

Вот вывод.
[sourcecode language=»php»]
array ( 0 => 'a', 'q' => 22, 's' => '142', 'dump' => array ( 'zero' => '5', 'dump' => 'foo', ), )
[/sourcecode]
Данный вывод можно скопировать в код и он будет рабочим. Так иногда можно вытащить динамический массив из кода, для дальнейшей работы с ним.

Эти функции прекрасны и без них было сложней работать, но у них есть большой недостаток. А именно, вывод больших массивов, тяжело читаем. К примеру из одного проекта.

print_r

Попробуйте в этой каше разобраться? Даже я, зная структуру данного объекта, не сразу могу найти нужное значение.

Для чтения больших массивов, лучше использовать другие инструменты.

2. krumo()

Если кратко

«Krumo представляет из себя парсер переменных и конфигурационной информации PHP, реализующий хорошо читаемый древовидный вывод сложных переменных и объектов этого языка.»(c) интернет

А если еще короче, то krumo() это замена “print_r() || var_dump() || var_export()”

Причем красивый и очень удобный аналог. В php по умолчанию его нет, скачать его можно тут

http://krumo.kaloyan.info

Работать с ним также легко, как и с “print_r() || var_dump() || var_export()”

Скачайте и распакуйте его в корень сайта

Сперва, надо провести начальную настройку, хоть это не обязательно, но так будет лучше.

Откройте krumo.ini.

Там всегда две строки которые надо изменить.

[sourcecode language=»text»]1. [skin]

selected = "schablon.com"

2.[css]

url = "http://www.example.com/Krumo/"
[/sourcecode]

В комплекте, идет 6 внешних видов вывода. Посмотреть их можно в папке skins. Выбирайте любой и вписывайте название в 1.

[sourcecode language=»text»]
1. [skin]
selected = "green"
[/sourcecode]
В 2, надо прописать путь к папки с krumo.
[sourcecode language=»text»][css]
url = "/krumo/"
[/sourcecode]
Вот и вся настройка. Krumo() будет работать и без этих настроек, просто с ними будет красивей и наглядней.

Для использования krumo(), выполняем
[sourcecode language=»php»]
include('krumo/class.krumo.php');
[/sourcecode]
После чего, просто и легко, пишем
[sourcecode language=»php»]
krumo($array);
[/sourcecode]
и получаем вот такую красоту

krumo()

На первой картинке видим, как изначально все выглядит. На второй картинке, мы видим что будет если раскрыть наш массив, клацнув на нем мышкой.

Поддерживается любой массив, объект, переменная и тп и тд, любой глубины и с любыми данными.

А вот так выглядит мой огромный объект

Я не стал его полностью раскрывать, он реально огромный. Но благодаря krumo(), я могу с легкостью увидеть свой массив данных в легко читаемом виде.

Krumo() поддерживает вывод сразу нескольких массивов за раз.
[sourcecode language=»php»]
krumo($array, $clone_array, $clone_array);
[/sourcecode]

Вот немного разобрались. Этих инструментов обычно хватает на 99%. Но все равно, этого мало. Иногда нужно знать еще больше. Надо знать не только о данных, но где и какая функция или класс, что приняли и куда дальше пошли, сколько времени работает та или иная функция и сколько занимают памяти.

Голыми средствами php этого не узнать, для этого надо сторонние библиотеки использовать. Верней можно, но настолько все будет сложно, что придется потратить очень много времени на реализацию этого. Лучше использовать специализированные библиотеки.

Встречайте

3. xDebug

xdebug – это расширение для отладки и трассировки кода в PHP, написанное Derick Rethans, одним из разработчиков языка PHP.

Если у вас вебсервер Denver, то данное расширение уже стоит.

Для других напишу как быстро установить.

1.Создаем файл (к примеру в корне localhost) phpinfo.php с содержимым
[sourcecode language=»php»]
phpinfo();
[/sourcecode]

2. После чего открываете www.localhost/phpinfo.php
Будет выведена страница с информацией о PHP, нам нужно открыть исходный кодстраницы. Для этого жмем ctrl+u, копируем весь исходный код страницы и вставляем на странице http://xdebug.org/wizard.php и жмем «Analyse my phpinfo() output»
Вам будет выдана информация, какой файл нужно качать и что писать в php.ini
3. Качаем рекомендуемый файл и сохраняем его на жестком диске куда вам хочется или в папку с расширениями php.
4. Открываем php.ini, посмотреть где он находится можно легко www.localhost/phpinfo.php, там можно найти информацию где лежат расширения для PHP.
Пишем в php.ini
[sourcecode language=»text»]
[xdebug]
zend_extension = \usr\local\php5\ext\php_xdebug-2.2.1-5.3-vc9.dll
[/sourcecode]
или
[sourcecode language=»text»]
[xdebug]
zend_extension_ts = \usr\local\php5\ext\php_xdebug-2.2.1-5.3-vc9.dll
[/sourcecode]
Зависит от версии PHP. Вам на странице http://xdebug.org/wizard.php указали что нужно писать.
5.  Перезагружаем webserver.
Проверить, правильно ли все, можно www.localhost/phpinfo.php
Там должно быть подобное

Установка завершена, переходим к настройке.

После строки с
[sourcecode language=»text»]
zend_extension
[/sourcecode]
в php.ini дописываем
[sourcecode language=»text»]
[xdebug]
zend_extension="\usr\local\php5\ext\php_xdebug-2.2.0-5.3-vc9.dll"
xdebug.default_enable = On
xdebug.profiler_enable = Off ;Off
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_dir = "/tmp/xdebug"
xdebug.profiler_append = On
xdebug.profiler_output_name = "xdebug.out.%t.%s"
xdebug.auto_trace = Off
xdebug.trace_format = Off
xdebug.collect_params = 1
xdebug.collect_return = 1
xdebug.collect_includes = 1
xdebug.trace_options = 1
xdebug.trace_output_dir = "/tmp/xdebug/trace"
[/sourcecode]
Обязательно создайте папки:
[sourcecode language=»text»]
xdebug.trace_output_dir = "/tmp/xdebug/trace"
xdebug.profiler_output_dir = "/tmp/xdebug"
[/sourcecode]
Или укажите свои.

Все готово для профилирования или трассировки.
Но начнем с var_dump и вывода ошибок. :-)
Благодаря xDebug, вывод ошибок и вывод var_dump стали более красивые и структурированными.
Пример ошибки

А вот так выводится var_dump()

Согласитесь, стало намного лучше, того что было.

Переходим к профилированию.

Профилирование

Профилирование — сбор характеристик работы программы, таких как время выполнения отдельных фрагментов (обычно подпрограмм), число верно предсказанных условных переходов, число кэш промахов и т. д. Инструмент, используемый для анализа работы, называют профилировщиком. Обычно выполняется совместно с оптимизацией программы. (c) Wikipedia

Так как профилирование очень дорогое удовольствие и не на каждой странице нужно проводить профилирование, профилирование по умолчанию отключено. Чтобы включить его для определенной страницы, надо в конец url добавить ?XDEBUG_PROFILE Это позволить запустить процесс профилирования. В корне сайта я сделал файл test.php c содержимым
[sourcecode language=»php]
$array = array('0'=>'a','q'=>22, 's'=>'142','dump'=>array('zero'=>'5','dump'=>'foo'));
include('krumo/class.krumo.php');
krumo($array);
[/sourcecode]

Заходим http://localhost/test.php?XDEBUG_PROFILE

В папке «tmp\xdebug» должен быть файл с длинным именем
[sourcecode language=»text]
cachegrind.out.1354193532.Z__home_localhost_www_test_php
[/sourcecode]
где
1354193532 — timestamp
Z_home_localhost_www_test_php — путь к скрипту который мы профилировали, слеш и точка с двоеточием, были заменены на нижние подчеркивание.

Если открыть данный файл, то там будет примерно 1000 строчек текста, в котором реально разобраться нельзя.

Для этого мы воспользуемся специальной утилитой WinCacheGrind
http://sourceforge.net/projects/wincachegrind/?source=dlp

Сходу понять, что и куда, довольно сложно, но на самом деле это на первый взгляд. По изучайте ваш результат профилирования и я думаю, вы поймете все сами.

Программа покажет, какие файлы были загружены, какие функции запущены и в какой последовательности, сколько времени выполнялись и что выполнялось. По изучайте самостоятельно, там все наглядно.

Файлы профилирования могут содержать не одну тысячу строк и могут занять часы на изучение. Но когда надо понять, где ошибка или где узкое место, это очень сильный инструмент.

Трассировка

Трассиро́вка — процесс пошагового выполнения программы. В режиме трассировки программист видит последовательность выполнения команд и значения переменных на данном шаге выполнения программы, что позволяет легче обнаруживать ошибки. Трассировка может быть начата и окончена в любом месте программы, выполнение программы может останавливаться на каждой команде или на точках останова, трассировка может выполняться с заходом в процедуры и без заходов. (c) Wikipedia

По умолчанию трассировка отключена. По той причине, что если начать трассировать весь сайт, это может закончится много километровыми логами, на изучение которого уйдет не один день. Чтобы лишние не трассировать, есть специальная функции
[sourcecode language=»php]
xdebug_start_trace(); //- начало трассирования.
xdebug_stop_trace(); //- конец трассирования.
[/sourcecode]
Если трассировать класс krumo(), то там будет вывод на несколько тысяч строк. Для примера, очень много, поэтому изменим немного наш код. Создадим функцию с рекурсией. Рекурсия одна из головных болей программиста, не всегда ясно, что она выполняет и когда заканчивается. Мы же создадим простую функцию с рекурсией, которая выводит значения массива в строку.
[sourcecode language=»php»]
$array = array('0'=>'a','q'=>22, 's'=>'142','dump'=>array('zero'=>'5','dump'=>'foo'));

xdebug_start_trace();

print recursion($array);

xdebug_stop_trace();

function recursion($array){

foreach ($array as $key => $value) {
if(is_array($value)){
$variable .= recursion($value);
}else{
$variable .= $value;
}
}
return $variable;
}
[/sourcecode]

Заходим http://localhost/test.php и в папке «/tmp/xdebug/trace», мы видим файл трассировки.
С ним сложней и проще одновременно.
Сложней, потому что нет спец.программ для его чтения.
Но проще, потому, что и не нужно.
Откройте файл трассировки в любом текстовом редакторе.

Мы видим пошаговое выполнение нашего кода.

  • Первая колонка это время выполнения скрипта. Значение в секундах
  • Вторая колонка, это используемая память на данный момент. Значение в байтах
  • Третья колонка это что именно на тот момент выполнялось.

Ничего сложного нет тут и после минуты изучения данного примера, можно спокойно понять, что происходит с кодом.

Выводить можно и в html формате. Для этого надо

заменить
[sourcecode language=»php»]
xdebug_start_trace();
//на
xdebug_start_trace('C:\test.html', XDEBUG_TRACE_HTML);
[/sourcecode]
Но информации выводится меньше. Поэтому лучше использовать первый вариант.

Я показал основы отладки кода в PHP.  Конечно это не все, но на данном этапе, вам хватит за глаза и этого.

P.S. Для просмотра логов профилирования, лучше использовать http://sourceforge.net/projects/xcallgraph/

Я его нашел уже после написания статьи, поэтому не стал исправлять.
Для Linux есть лучше вариант, это kcachegrind. Для Windows он тоже есть, но работает кривовато.

Поделиться

Комментарии Правила дискуссии

Читайте ранее:
Редирект

Периодически у вас возникает потребность перенаправить посетителя сайта на другой адрес. К примеру, у вас несколько доменов для одного сайта,...

Закрыть