Не работает скрипт? Настало время отлаживать. Программы, которые нас не убивают, делают нас сильнее!
Это статья открывает серию статей по отладке php-скриптов.
Для начала давайте рассмотрим те средства отладки, которые доступны сразу (без установки дополнительного ПО).
Часть этой информации окажется не интересной для вас, если вы не новичок. Но блог рассчитан на людей с разными знаниями. Поэтому начнем с информации для новичков. Гуру же могут прокрутить к интересному или повторить.
Самой первой информацией помогающей программисту найти ошибку в скрипте является сообщение об ошибке. Если скрипт не работает, но ошибок нет на странице убедитесь, что в php.ini включен вывод ошибок. Если же у вас нет доступа к данному файлу, то вы можете включить вывод ошибок для своего скрипта. Для этого добавьте в самое начало скрипта 2 строки:

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

Часто после изучения сообщения об ошибке сразу становится ясно в чем дело. Но если этого не произошло, для отладки может оказаться полезным посмотреть данные с которыми вы работаете до появление ошибки. Вы можете просматривать как простые типы данных так и структуры.
Для этих целей можно использовать echo(), print(), print_r(), var_dump() и var_export ().
Наверняка с echo() и print() все понятно - они просто выводят содержимое переменных с простыми типами (строки, числа). Для различного рода структур же прекрасно подойдут print_r() и var_dump().
Классический пример:

$a = array ('a' => 'apple', 'b' => 'banana', 'c' => array ('x', 'y', 'z'));
print_r ($a);

Только поместите вывод print_r() внутрь тега pre, чтобы вы могли видеть массив в удобочитаемом виде прямо в браузере.

Array
(
    [a] => apple
    [b] => banana
    [c] => Array
        (
            [0] => x
            [1] => y
            [2] => z
        )
)

Таким образом мы получили структуру массива в наглядном виде. Для этого и предназначена функция print_r().
Она выводит информацию о переменной в удобочитаемом виде.
Функция var_dump() отображает структурированную информацию об одном или нескольких выражениях, которая включает в себя их тип и значение. Массивы и объекты анализируются рекурсивно с разным отступом у значений для визуального отображения структуры.
В PHP 5 все общедоступные, закрытые и защищенные свойства объекта будут возвращены при выводе.

$a = array(1, 2, array("a", "b", "c"));
var_dump($a);
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  array(3) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    string(1) "c"
  }
}

Если же надо получить представление в php-коде, то тут приходит на помощь функция var_export ().
В остальном она аналогична предыдущей функции.

Дальше часть из тех кому было пока что скучно присоединится к нам. Потому что речь пойдет об уже более мощном инструменте. Таком как dBug .
Что же дает dBug для улучшения жизни при отладке? И почему не продолжать пользоваться традиционными echo() и print_r() ? А все потому, что структура, которую мы просматриваем может оказаться куда как сложнее чем приведенные выше примеры. Чтобы разобраться в больших структурах при отладке и служит dBug.
Вот как это выглядит в работе:

include_once("dBug.php");
$variable = array(
    "first"=>"1",
    "second",
    "third"=>array(
        "inner third 1",
        "inner third 2"=>"yeah"),
    "fourth" => array(
	'a' => array(
		'a1' => 2345, 
		'a2'=> 45),
	'b' => array(
		'b1' => array(1, 2, 3, 4)),
	 'c' => 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. '.
		'Morbi vitae justo. Fusce mattis leo at pede. '.
		'Suspendisse ipsum. In vel mauris.'));
 
new dBug($variable);

Аналогичным образом выводится и дамп объекта.
Еще одной хорошей особенностью является возможность просмотра результатов выборки из базы данных. Делается это следующим образом:

$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link)
   die('Could not connect: ' . mysql_error());
 
mysql_select_db('mydb');
$result = mysql_query('select * from empinfo');
new dBug($result);

И вот что получаем:


Кстати вложенные уровни вы можете свернуть щелчком мыши, что и помогает при просмотре больших структур. В таком же виде библиотека может отображать и xml-данные. Не правда ли удобнее?

Если в dBug используются таблицы, то в krumo используется дерево. Знакомьтесь с еще одним отладчиком.
Подключается он так:

include "class.krumo.php";

Используется так:

// Показ debug backgrace
 krumo::backtrace();
 // Показ всех подключенных классов
 krumo::includes();
 // Показ всех функций
 krumo::functions();
 // Показ всех декларированных классов
 krumo::classes();
 // Показ всех объявленных констант
 krumo::defines();
 // Запретить вывод отладочных сообщений всего что ниже 
 krumo::disable();
 // Этот вывод показан не будет
 krumo::includes();
 // Разрешить отладочный вывод
 krumo::enable();
 // Этот вывод будет показан
 krumo::classes();

Этот инструмент поддерживает даже шкурки ( к нему можно подключить разные стили оформления). На сайте можно скачать несколько таких шкурок.
Полный список методов можно найти на странице документации krumo.
Создадим массив:

krumo(array('a1'=> 'A1', 3, 'red'));

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

В фреймворке PEAR есть класс Var_Dump, который так же служит оберткой для var_dump(). Его функции отображают структурированную информацию.
Класс может отображать информацию в следующих видах :

  • Простой текст
  • HTML/XHTML текст
  • HTML/XHTML таблицы
  • XML

А что если у вашего клиента на живом сайте что-то не работает, а воспроизвести ситуацию локально вам не удается? Вам будет легче попробовать это прямо на живом сервере.
Но как быть с посетителями? Вероятно они будут удивлены увидев непонятные символы и структуры на экране вместо привычной web-страницы. И даже если вы оденете krumo в самый элегантный скин, конфуз нисколько не будет меньше.
Как же в этом случае вам делать отладку? Один из таких инструментов - это класс Debug_HackerConsole_Main, который разработан dkLab (Дмитрий Котеров).
Он позволяет вставить на страницу хакерскую консоль. Пользователь будет видеть обычную страницу, но вы сможете нажать Ctrl+~ (Ctrl и тильда) и перед вами распахнется консоль с отладочной информацией.
Класс имеет следующие особенности:

  • Все сообщения, выводимые в консоль, объединяются в группы (для этого служит второй параметр метода out()) для удобства отображения
  • Можно задавать цвет текста выводимых сообщений (для этого служит третий параметр)
  • Есть высплывающая подсказка, в которой написано, в каком файле и на какой строке сгенерировано сообщение
  • В скрипте можно создавать несколько консолей

Но возможно кто-то данный инструмент найдет не совсем удобным. Для отладки javascript люди используют fireBug. Выводить бы отладочную информацию туда же… В принципе задача реальная.
Для того чтобы просматривать отладочную информацию в любимой консоли можно использовать расширение для fireFox, которое называется firePHP (требуется установленный fireBug). Отладочная информация передается браузеру не в теле страницы а в http-заголовках.
Подключаем библиотеку так:

require_once('FirePHPCore/FirePHP.class.php');
$firephp = FirePHP::getInstance(true);

После подключения данной библиотеки вам доступен ряд методов класса.

$firephp->log('Log message');
$firephp->info('Info message');
$firephp->warn('Warn message');
$firephp->error('Error message');

В консоли fireBug вы увидите аналогичные сообщения. И это далеко не все!

Вы можете создавать группы

$firephp->group('Test Group');
$firephp->log('Hello World');
$firephp->groupEnd();


Вы можете выводить сообщения в консоль при возникновении исключительных ситуаций

$firephp->registerErrorHandler();
$firephp->registerExceptionHandler();
try {
  throw new Exception('Test Exception');
} catch(Exception $e) {
  $firephp->error($e);  // or FB::
}

Вы можете производить трассировку

$firephp->trace('Trace Label');

Смотреть дамп переменной

$firephp->dump('Key', $variable);

Так же вы можете отображать информацию виде таблиц

$table   = array();
$table[] = array('Col 1 Heading','Col 2 Heading');
$table[] = array('Row 1 Col 1','Row 1 Col 2');
$table[] = array('Row 2 Col 1','Row 2 Col 2');
$table[] = array('Row 3 Col 1','Row 3 Col 2'); 
$firephp->table('Table Label', $table);


Существует ряд опций

  • maxObjectDepth - максимальная глубина изучения объекта
  • maxArrayDepth - максимальная глубина изучения массива
  • useNativeJsonEncode - установите FALSE для поддержки ISO-8859-1 (Latin-1). Будет использован JSON encoder включонный в FirePHPCore вместо json_encode()
  • includeLineNumbers - включает в сообщения информацию о файле и строке

Установка опций происходит так

// Defaults:
$options = array('maxObjectDepth' = 10,
                 'maxArrayDepth' = 20,
                 'useNativeJsonEncode' = true,
                 'includeLineNumbers' = true);
 
$firephp->setOptions(options);

Есть возможность исключить из вывода конкретные члены классов.

$firephp->setObjectFilter('ClassName', array('MemberName'));

Стоит всегда помнить, что данные для firePHP отправляются в заголовках страницы (которые должны быть отправлены раньше содержимого страницы). Поэтому если отладочные массивы у вас выводятся где-то в середине генерации кода страницы, то используйте пару функций. Первая из них ob_start() - начинает запись в буфер. Вторая - ob_end_flush() - отправляет данные из буфера в поток. Схема такая : ob_start() - пользуемся firephp, echo и другими методами вывода - ob_end_flush().

На сайте habrahabr.ru один из пользователей выложил свою библиотеку альтернативу firePHP, которая так же может выводить отладочную информацию в консоль fireBug, но генерирует свой код (js) прямо в тело страницы.
Выглядит в работе инструмент так:
Альтернатива firePHP в действии

Вы можете скачать пример и попробовать этот инструмент самостоятельно.

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

Tags: ,

9 Responses to “Отладка php - echo, print_r, var_dump, dBug, krumo, firePHP”

  1.  solarisadmin Says:

    Спасибо, про FirePHP не знал

  2.  Evgen Says:

    Работаю с php 3 года. По мне так кроме echo, print_r да var_export ничего не нужно. Это как тот же smarty - смысл его использовать, если php сам лучший шаблонизатор.

  3.  Игорь Тельменко Says:

    Зависит от сложности разрабатываемых приложений и степени стремления делать работу быстрее… Я, например, все чаще использую связь eclipse+xdebug для отладки.

  4.  Игорь Тельменко Says:

    К тому же на хорошо посещаемом сайте вы print_r делать не станете.

  5.  Evgen Says:

    Я использую VIM c довольно внушительным .vimrc =)

  6.  Evgen Says:

    Даже на слабо посещаемом ресурсе копирую сырцы этого самого ресурса с дальнейшим издевательством на localhost =)

  7.  Evgen Says:

    Ждемс статьи по обработке ошибок. Как вы предпочитаете это делать? Лично я использую юзаю trigger_error(). Самописный класс обработки ошибок, основной сутью является возможность вести логи при установленной в конфиге опции DEBUG. 1 - ошибки пишутся в логи, 0 соответственно выдаются в браузер. Как-то с недоверием я отношусь к Exception. Дело в том, что зачастую приходится обработать ошибку в месте вероятного её появления и при этом необходимо послать в вызвавший код разультат отработки данного участка, чтобы вызвавший код смог приспокойно определить дальнейшие действия (как правило выдача в браузер заранее определенного шаблона).
    Не вижу никаких недостатков приведенного мною способа перед Exception. Хотя возможно и ошибаюсь. Единственный + использования Exception’-ов так это при работе с БД. Собственно лишь по той причине, что само сообщение об ошибке генерирует сам СУБД.
    Ждем Вашего подхода =).

  8.  Игорь Тельменко Says:

    Даже на слабо посещаемом ресурсе копирую сырцы этого самого ресурса с дальнейшим издевательством на localhost =)

    Опять же все зависит от величины и сложности проекта. на солидных проектах проще использовать более совершенные приемы отладки, чем воспроизводить локально ситуацию, которая возникает на живом сайте.
    Я даже не копирую на локал. Я там разрабатываю. но частенько ситуация на живом сайте отличается от локала.

  9.  Игорь Тельменко Says:

    Ждемс статьи по обработке ошибок. Как вы предпочитаете это делать?
    Тут без статьи. Просто использую фреймворк Kohana. Там уже есть и логирование и раскрутка. ;)